aboutsummaryrefslogtreecommitdiff
path: root/src/mintdb/plugin_mintdb_postgres.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mintdb/plugin_mintdb_postgres.c')
-rw-r--r--src/mintdb/plugin_mintdb_postgres.c224
1 files changed, 180 insertions, 44 deletions
diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c
index 6e26c82e..1347875e 100644
--- a/src/mintdb/plugin_mintdb_postgres.c
+++ b/src/mintdb/plugin_mintdb_postgres.c
@@ -424,15 +424,10 @@ postgres_create_tables (void *cls,
",ev_sig BYTEA NOT NULL"
")");
/* This table contains the wire transfers the mint is supposed to
- execute to transmit funds to the merchants (and manage refunds).
- TODO: we might want to generate some other primary key
- to internally identify outgoing transactions, as "coin_pub"
- may not be unique if a wallet chooses not to refresh. The
- resulting transaction ID should then be returned to the merchant
- and could be used by the mearchant for further inquriries about
- the deposit's execution. (#3816); */
+ execute to transmit funds to the merchants (and manage refunds). */
SQLEXEC("CREATE TABLE IF NOT EXISTS deposits "
- "(coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)"
+ "(serial_id BIGSERIAL"
+ ",coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)"
",denom_pub BYTEA NOT NULL REFERENCES denominations (pub)"
",denom_sig BYTEA NOT NULL"
",transaction_id INT8 NOT NULL"
@@ -598,7 +593,7 @@ postgres_prepare (PGconn *db_conn)
1, NULL);
/* Used in #postgres_insert_withdraw_info() to store
the signature of a blinded coin with the blinded coin's
- details before returning it during /withdraw/sign. We store
+ details before returning it during /reserve/withdraw. We store
the coin's denomination information (public key, signature)
and the blinded message as well as the reserve that the coin
is being withdrawn from and the signature of the message
@@ -621,9 +616,9 @@ postgres_prepare (PGconn *db_conn)
"($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12);",
12, NULL);
/* Used in #postgres_get_withdraw_info() to
- locate the response for a /withdraw/sign request
+ locate the response for a /reserve/withdraw request
using the hash of the blinded message. Used to
- make sure /withdraw/sign requests are idempotent. */
+ make sure /reserve/withdraw requests are idempotent. */
PREPARE ("get_withdraw_info",
"SELECT"
" denom_pub"
@@ -641,7 +636,7 @@ postgres_prepare (PGconn *db_conn)
" WHERE h_blind_ev=$1",
1, NULL);
/* Used during #postgres_get_reserve_history() to
- obtain all of the /withdraw/sign operations that
+ obtain all of the /reserve/withdraw operations that
have been performed on a given reserve. (i.e. to
demonstrate double-spending) */
PREPARE ("get_reserves_out",
@@ -853,6 +848,25 @@ postgres_prepare (PGconn *db_conn)
" (merchant_pub=$3)"
" )",
3, NULL);
+
+ /* Used in #postgres_iterate_deposits() */
+ PREPARE ("deposits_iterate",
+ "SELECT"
+ " serial_id"
+ ",amount_with_fee_val"
+ ",amount_with_fee_frac"
+ ",amount_with_fee_curr"
+ ",deposit_fee_val"
+ ",deposit_fee_frac"
+ ",deposit_fee_curr"
+ ",transaction_id"
+ ",h_contract"
+ ",wire"
+ " FROM deposits"
+ " WHERE serial_id>=$1"
+ " ORDER BY serial_id ASC"
+ " LIMIT $2;",
+ 2, NULL);
/* Used in #postgres_get_coin_transactions() to obtain information
about how a coin has been spend with /deposit requests. */
PREPARE ("get_deposit_with_coin_pub",
@@ -976,7 +990,7 @@ postgres_get_session (void *cls,
PQstatus (db_conn))
{
TALER_LOG_ERROR ("Database connection failed: %s\n",
- PQerrorMessage (db_conn));
+ PQerrorMessage (db_conn));
GNUNET_break (0);
return NULL;
}
@@ -1081,7 +1095,31 @@ postgres_commit (void *cls,
if (PGRES_COMMAND_OK !=
PQresultStatus (result))
{
- GNUNET_break (0);
+ const char *sqlstate;
+
+ sqlstate = PQresultErrorField (result,
+ PG_DIAG_SQLSTATE);
+ if (NULL == sqlstate)
+ {
+ /* very unexpected... */
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ /* 40P01: deadlock, 40001: serialization failure */
+ if ( (0 == strcmp (sqlstate,
+ "40P01")) ||
+ (0 == strcmp (sqlstate,
+ "40001")) )
+ {
+ /* These two can be retried and have a fair chance of working
+ the next time */
+ PQclear (result);
+ return GNUNET_NO;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Database commit failure: %s\n",
+ sqlstate);
PQclear (result);
return GNUNET_SYSERR;
}
@@ -1488,7 +1526,7 @@ postgres_reserves_in_insert (void *cls,
/**
- * Locate the response for a /withdraw/sign request under the
+ * Locate the response for a /reserve/withdraw request under the
* key of the hash of the blinded message.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
@@ -1882,6 +1920,119 @@ postgres_have_deposit (void *cls,
/**
+ * Obtain information about deposits. Iterates over all deposits
+ * above a certain ID. Use a @a min_id of 0 to start at the beginning.
+ * This operation is executed in its own transaction in transaction
+ * mode "REPEATABLE READ", i.e. we should only see valid deposits.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param session connection to the database
+ * @param min_id deposit to start at
+ * @param limit maximum number of transactions to fetch
+ * @param deposit_cb function to call for each deposit
+ * @param deposit_cb_cls closure for @a deposit_cb
+ * @return number of rows processed, 0 if none exist,
+ * #GNUNET_SYSERR on error
+ */
+static int
+postgres_iterate_deposits (void *cls,
+ struct TALER_MINTDB_Session *session,
+ uint64_t min_id,
+ uint32_t limit,
+ TALER_MINTDB_DepositIterator deposit_cb,
+ void *deposit_cb_cls)
+{
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_query_param_uint64 (&min_id),
+ TALER_PQ_query_param_uint32 (&limit),
+ TALER_PQ_query_param_end
+ };
+ PGresult *result;
+ unsigned int i;
+ unsigned int n;
+
+ if (GNUNET_OK !=
+ postgres_start (cls, session))
+ return GNUNET_SYSERR;
+ result = PQexec (session->conn,
+ "SET TRANSACTION REPEATABLE READ");
+ if (PGRES_COMMAND_OK !=
+ PQresultStatus (result))
+ {
+ TALER_LOG_ERROR ("Failed to set transaction to REPEATABL EREAD: %s\n",
+ PQresultErrorMessage (result));
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ result = TALER_PQ_exec_prepared (session->conn,
+ "deposits_iterate",
+ params);
+ if (PGRES_TUPLES_OK !=
+ PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ PQclear (result);
+ postgres_rollback (cls, session);
+ return GNUNET_SYSERR;
+ }
+ if (0 == (n = PQntuples (result)))
+ {
+ PQclear (result);
+ postgres_rollback (cls, session);
+ return 0;
+ }
+ for (i=0;i<n;i++)
+ {
+ struct TALER_Amount amount_with_fee;
+ struct TALER_Amount deposit_fee;
+ struct GNUNET_HashCode h_contract;
+ json_t *wire;
+ uint64_t transaction_id;
+ uint64_t id;
+ int ret;
+ struct TALER_PQ_ResultSpec rs[] = {
+ TALER_PQ_result_spec_uint64 ("id",
+ &id),
+ TALER_PQ_result_spec_uint64 ("transaction_id",
+ &transaction_id),
+ TALER_PQ_result_spec_amount ("amount_with_fee",
+ &amount_with_fee),
+ TALER_PQ_result_spec_amount ("deposit_fee",
+ &deposit_fee),
+ TALER_PQ_result_spec_auto_from_type ("h_contract",
+ &h_contract),
+ TALER_PQ_result_spec_json ("wire",
+ &wire),
+ TALER_PQ_result_spec_end
+ };
+ if (GNUNET_OK !=
+ TALER_PQ_extract_result (result, rs, i))
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ postgres_rollback (cls, session);
+ return GNUNET_SYSERR;
+ }
+ ret = deposit_cb (deposit_cb_cls,
+ id,
+ &amount_with_fee,
+ &deposit_fee,
+ transaction_id,
+ &h_contract,
+ wire);
+ TALER_PQ_cleanup_result (rs);
+ PQclear (result);
+ if (GNUNET_OK != ret)
+ break;
+ }
+ postgres_rollback (cls, session);
+ return i;
+}
+
+
+/**
* Insert information about deposited coin into the database.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
@@ -2122,12 +2273,15 @@ get_known_coin (void *cls,
return GNUNET_YES;
{
struct TALER_PQ_ResultSpec rs[] = {
- TALER_PQ_result_spec_rsa_public_key ("denom_pub", &coin_info->denom_pub.rsa_public_key),
- TALER_PQ_result_spec_rsa_signature ("denom_sig", &coin_info->denom_sig.rsa_signature),
+ TALER_PQ_result_spec_rsa_public_key ("denom_pub",
+ &coin_info->denom_pub.rsa_public_key),
+ TALER_PQ_result_spec_rsa_signature ("denom_sig",
+ &coin_info->denom_sig.rsa_signature),
TALER_PQ_result_spec_end
};
- if (GNUNET_OK != TALER_PQ_extract_result (result, rs, 0))
+ if (GNUNET_OK !=
+ TALER_PQ_extract_result (result, rs, 0))
{
PQclear (result);
GNUNET_break (0);
@@ -2277,7 +2431,11 @@ postgres_get_refresh_melt (void *cls,
&coin))
return GNUNET_SYSERR;
if (NULL == melt)
+ {
+ GNUNET_CRYPTO_rsa_signature_free (coin.denom_sig.rsa_signature);
+ GNUNET_CRYPTO_rsa_public_key_free (coin.denom_pub.rsa_public_key);
return GNUNET_OK;
+ }
melt->coin = coin;
melt->coin_sig = coin_sig;
melt->session_hash = *session_hash;
@@ -2666,7 +2824,7 @@ postgres_insert_refresh_commit_links (void *cls,
PQclear (result);
return GNUNET_SYSERR;
}
-
+
if (0 != strcmp ("1", PQcmdTuples (result)))
{
GNUNET_break (0);
@@ -2734,7 +2892,7 @@ postgres_get_refresh_commit_links (void *cls,
&links[i].shared_secret_enc),
TALER_PQ_result_spec_end
};
-
+
if (GNUNET_YES !=
TALER_PQ_extract_result (result, rs, 0))
{
@@ -2823,28 +2981,7 @@ postgres_get_melt_commitment (void *cls,
return mc;
cleanup:
- GNUNET_free_non_null (mc->melts);
- if (NULL != mc->denom_pubs)
- {
- for (i=0;i<(unsigned int) mc->num_newcoins;i++)
- if (NULL != mc->denom_pubs[i].rsa_public_key)
- GNUNET_CRYPTO_rsa_public_key_free (mc->denom_pubs[i].rsa_public_key);
- GNUNET_free (mc->denom_pubs);
- }
- for (cnc_index=0;cnc_index<TALER_CNC_KAPPA;cnc_index++)
- {
- if (NULL != mc->commit_coins[cnc_index])
- {
- for (i=0;i<(unsigned int) mc->num_newcoins;i++)
- {
- GNUNET_free_non_null (mc->commit_coins[cnc_index][i].refresh_link);
- GNUNET_free_non_null (mc->commit_coins[cnc_index][i].coin_ev);
- }
- GNUNET_free (mc->commit_coins[cnc_index]);
- }
- GNUNET_free_non_null (mc->commit_links[cnc_index]);
- }
- GNUNET_free (mc);
+ common_free_melt_commitment (cls, mc);
return NULL;
}
@@ -3267,6 +3404,7 @@ libtaler_plugin_mintdb_postgres_init (void *cls)
plugin->get_reserve_history = &postgres_get_reserve_history;
plugin->free_reserve_history = &common_free_reserve_history;
plugin->have_deposit = &postgres_have_deposit;
+ plugin->iterate_deposits = &postgres_iterate_deposits;
plugin->insert_deposit = &postgres_insert_deposit;
plugin->get_refresh_session = &postgres_get_refresh_session;
@@ -3286,8 +3424,6 @@ libtaler_plugin_mintdb_postgres_init (void *cls)
plugin->get_link_data_list = &postgres_get_link_data_list;
plugin->free_link_data_list = &common_free_link_data_list;
plugin->get_transfer = &postgres_get_transfer;
- // plugin->have_lock = &postgres_have_lock; /* #3625 */
- // plugin->insert_lock = &postgres_insert_lock; /* #3625 */
plugin->get_coin_transactions = &postgres_get_coin_transactions;
plugin->free_coin_transaction_list = &common_free_coin_transaction_list;
return plugin;