summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--src/backend/taler-merchant-httpd_check-payment.c8
-rw-r--r--src/backend/taler-merchant-httpd_exchanges.c4
-rw-r--r--src/backend/taler-merchant-httpd_history.c3
-rw-r--r--src/backend/taler-merchant-httpd_pay.c5
-rw-r--r--src/backend/taler-merchant-httpd_proposal.c4
-rw-r--r--src/backend/taler-merchant-httpd_refund.c2
-rw-r--r--src/backend/taler-merchant-httpd_tip-authorize.c1
-rw-r--r--src/backend/taler-merchant-httpd_tip-pickup.c1
-rw-r--r--src/backend/taler-merchant-httpd_tip-query.c44
-rw-r--r--src/backend/taler-merchant-httpd_track-transaction.c20
-rw-r--r--src/backend/taler-merchant-httpd_track-transfer.c11
-rw-r--r--src/backenddb/plugin_merchantdb_postgres.c67
-rw-r--r--src/include/taler_merchantdb_plugin.h16
14 files changed, 141 insertions, 46 deletions
diff --git a/.gitignore b/.gitignore
index 9ebe683f..661e10fb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -58,3 +58,4 @@ contrib/taler-merchant.tag
src/merchant-tools/taler-merchant-tip-enable
src/lib/reserve_dkey.priv
src/lib/reserve_key.priv
+doc/version.texi
diff --git a/src/backend/taler-merchant-httpd_check-payment.c b/src/backend/taler-merchant-httpd_check-payment.c
index 8366186c..60cd0351 100644
--- a/src/backend/taler-merchant-httpd_check-payment.c
+++ b/src/backend/taler-merchant-httpd_check-payment.c
@@ -58,7 +58,10 @@ process_refunds_cb (void *cls,
{
struct TALER_Amount *acc_amount = cls;
- GNUNET_assert (TALER_amount_add (acc_amount, acc_amount, refund_amount));
+ GNUNET_assert (GNUNET_SYSERR !=
+ TALER_amount_add (acc_amount,
+ acc_amount,
+ refund_amount));
}
@@ -187,6 +190,7 @@ MH_handler_check_payment (struct TMH_RequestHandler *rh,
GNUNET_assert (NULL != order_id);
+ db->preflight (db->cls);
qs = db->find_contract_terms (db->cls,
&contract_terms,
&last_session_id,
@@ -250,7 +254,7 @@ MH_handler_check_payment (struct TMH_RequestHandler *rh,
/* Check if paid */
{
json_t *xcontract_terms = NULL;
-
+
qs = db->find_paid_contract_terms_from_hash (db->cls,
&xcontract_terms,
&h_contract_terms,
diff --git a/src/backend/taler-merchant-httpd_exchanges.c b/src/backend/taler-merchant-httpd_exchanges.c
index c82e8319..1c90f001 100644
--- a/src/backend/taler-merchant-httpd_exchanges.c
+++ b/src/backend/taler-merchant-httpd_exchanges.c
@@ -360,8 +360,10 @@ process_wire_fees (void *cls,
wire_method,
GNUNET_STRINGS_absolute_time_to_string (af->start_date),
TALER_amount2s (&af->wire_fee));
+ db->preflight (db->cls);
if (GNUNET_OK !=
- db->start (db->cls))
+ db->start (db->cls,
+ "store wire fee"))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to start database transaction!\n");
diff --git a/src/backend/taler-merchant-httpd_history.c b/src/backend/taler-merchant-httpd_history.c
index 53adf30e..207ddcaf 100644
--- a/src/backend/taler-merchant-httpd_history.c
+++ b/src/backend/taler-merchant-httpd_history.c
@@ -174,8 +174,9 @@ MH_handler_history (struct TMH_RequestHandler *rh,
"instance");
}
- /* Here goes the cherry-picking logic */
+ db->preflight (db->cls);
+ /* Here goes the cherry-picking logic */
str = MHD_lookup_connection_value (connection,
MHD_GET_ARGUMENT_KIND,
"order_id");
diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c
index b112edf5..eee23c12 100644
--- a/src/backend/taler-merchant-httpd_pay.c
+++ b/src/backend/taler-merchant-httpd_pay.c
@@ -1651,7 +1651,10 @@ begin_transaction (struct PayContext *pc)
GNUNET_assert (GNUNET_YES == pc->suspended);
/* First, try to see if we have all we need already done */
- if (GNUNET_OK != db->start (db->cls))
+ db->preflight (db->cls);
+ if (GNUNET_OK !=
+ db->start (db->cls,
+ "run pay"))
{
GNUNET_break (0);
resume_pay_with_error (pc,
diff --git a/src/backend/taler-merchant-httpd_proposal.c b/src/backend/taler-merchant-httpd_proposal.c
index 60f8941d..64517b38 100644
--- a/src/backend/taler-merchant-httpd_proposal.c
+++ b/src/backend/taler-merchant-httpd_proposal.c
@@ -580,7 +580,6 @@ MH_handler_proposal_lookup (struct TMH_RequestHandler *rh,
return TMH_RESPONSE_reply_arg_missing (connection,
TALER_EC_PARAMETER_MISSING,
"instance");
-
mi = TMH_lookup_instance (instance);
if (NULL == mi)
return TMH_RESPONSE_reply_not_found (connection,
@@ -593,7 +592,6 @@ MH_handler_proposal_lookup (struct TMH_RequestHandler *rh,
return TMH_RESPONSE_reply_arg_missing (connection,
TALER_EC_PARAMETER_MISSING,
"order_id");
-
nonce = MHD_lookup_connection_value (connection,
MHD_GET_ARGUMENT_KIND,
"nonce");
@@ -601,7 +599,7 @@ MH_handler_proposal_lookup (struct TMH_RequestHandler *rh,
return TMH_RESPONSE_reply_arg_missing (connection,
TALER_EC_PARAMETER_MISSING,
"nonce");
-
+ db->preflight (db->cls);
qs = db->find_contract_terms (db->cls,
&contract_terms,
&last_session_id,
diff --git a/src/backend/taler-merchant-httpd_refund.c b/src/backend/taler-merchant-httpd_refund.c
index 8a6060c3..7498cd41 100644
--- a/src/backend/taler-merchant-httpd_refund.c
+++ b/src/backend/taler-merchant-httpd_refund.c
@@ -181,6 +181,8 @@ MH_handler_refund_increase (struct TMH_RequestHandler *rh,
"Unknown instance given");
}
+ db->preflight (db->cls);
+
/* Convert order id to h_contract_terms */
qs = db->find_contract_terms (db->cls,
&contract_terms,
diff --git a/src/backend/taler-merchant-httpd_tip-authorize.c b/src/backend/taler-merchant-httpd_tip-authorize.c
index 03d8ed3a..ebef3440 100644
--- a/src/backend/taler-merchant-httpd_tip-authorize.c
+++ b/src/backend/taler-merchant-httpd_tip-authorize.c
@@ -370,6 +370,7 @@ MH_handler_tip_authorize (struct TMH_RequestHandler *rh,
"exchange for tipping not configured for the instance");
}
tac->reserve_priv = mi->tip_reserve;
+ db->preflight (db->cls);
ec = db->authorize_tip (db->cls,
tac->justification,
&tac->amount,
diff --git a/src/backend/taler-merchant-httpd_tip-pickup.c b/src/backend/taler-merchant-httpd_tip-pickup.c
index 3cc73af5..7b3403d2 100644
--- a/src/backend/taler-merchant-httpd_tip-pickup.c
+++ b/src/backend/taler-merchant-httpd_tip-pickup.c
@@ -554,6 +554,7 @@ MH_handler_tip_pickup (struct TMH_RequestHandler *rh,
TALER_EC_PARAMETER_MALFORMED,
"no planchets specified");
}
+ db->preflight (db->cls);
pc->planchets = GNUNET_new_array (pc->planchets_len,
struct PlanchetDetail);
for (unsigned int i=0;i<pc->planchets_len;i++)
diff --git a/src/backend/taler-merchant-httpd_tip-query.c b/src/backend/taler-merchant-httpd_tip-query.c
index 97d3fd13..bdf93bef 100644
--- a/src/backend/taler-merchant-httpd_tip-query.c
+++ b/src/backend/taler-merchant-httpd_tip-query.c
@@ -222,7 +222,9 @@ handle_status (void *cls,
return;
}
- if (GNUNET_OK != TALER_amount_get_zero (history[0].amount.currency, &tqc->amount_withdrawn))
+ if (GNUNET_OK !=
+ TALER_amount_get_zero (history[0].amount.currency,
+ &tqc->amount_withdrawn))
{
GNUNET_break_op (0);
resume_with_response (tqc, MHD_HTTP_SERVICE_UNAVAILABLE,
@@ -232,8 +234,8 @@ handle_status (void *cls,
}
if (GNUNET_YES == tqc->none_authorized)
- memcpy (&tqc->amount_authorized, &tqc->amount_withdrawn, sizeof (struct TALER_Amount));
- memcpy (&tqc->amount_deposited, &tqc->amount_withdrawn, sizeof (struct TALER_Amount));
+ tqc->amount_authorized = tqc->amount_withdrawn;
+ tqc->amount_deposited = tqc->amount_withdrawn;
/* Update DB based on status! */
for (unsigned int i=0;i<history_length;i++)
@@ -255,12 +257,14 @@ handle_status (void *cls,
&uuid,
&history[i].amount,
expiration);
- if (GNUNET_OK != TALER_amount_add (&tqc->amount_deposited,
- &tqc->amount_deposited,
- &history[i].amount))
+ if (GNUNET_OK !=
+ TALER_amount_add (&tqc->amount_deposited,
+ &tqc->amount_deposited,
+ &history[i].amount))
{
GNUNET_break_op (0);
- resume_with_response (tqc, MHD_HTTP_SERVICE_UNAVAILABLE,
+ resume_with_response (tqc,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
TMH_RESPONSE_make_error (TALER_EC_NONE /* FIXME */,
"Exchange returned invalid reserve history (amount overflow)"));
return;
@@ -275,12 +279,14 @@ handle_status (void *cls,
}
break;
case TALER_EXCHANGE_RTT_WITHDRAWAL:
- if (GNUNET_OK != TALER_amount_add (&tqc->amount_withdrawn,
- &tqc->amount_withdrawn,
- &history[i].amount))
+ if (GNUNET_OK !=
+ TALER_amount_add (&tqc->amount_withdrawn,
+ &tqc->amount_withdrawn,
+ &history[i].amount))
{
GNUNET_break_op (0);
- resume_with_response (tqc, MHD_HTTP_SERVICE_UNAVAILABLE,
+ resume_with_response (tqc,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
TMH_RESPONSE_make_error (TALER_EC_NONE /* FIXME */,
"Exchange returned invalid reserve history (amount overflow)"));
return;
@@ -300,20 +306,27 @@ handle_status (void *cls,
{
struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub;
struct TALER_Amount amount_available;
+
GNUNET_CRYPTO_eddsa_key_get_public (&tqc->reserve_priv.eddsa_priv,
&reserve_pub);
- if (GNUNET_SYSERR == TALER_amount_subtract (&amount_available, &tqc->amount_deposited, &tqc->amount_withdrawn))
+ if (GNUNET_SYSERR ==
+ TALER_amount_subtract (&amount_available,
+ &tqc->amount_deposited,
+ &tqc->amount_withdrawn))
{
GNUNET_break_op (0);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "amount overflow, deposited %s but withdrawn %s\n",
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "amount overflow, deposited %s but withdrawn %s\n",
TALER_amount_to_string (&tqc->amount_deposited),
TALER_amount_to_string (&tqc->amount_withdrawn));
- resume_with_response (tqc, MHD_HTTP_SERVICE_UNAVAILABLE,
+ resume_with_response (tqc,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
TMH_RESPONSE_make_error (TALER_EC_NONE /* FIXME */,
"Exchange returned invalid reserve history (amount overflow)"));
}
- resume_with_response (tqc, MHD_HTTP_OK,
+ resume_with_response (tqc,
+ MHD_HTTP_OK,
TMH_RESPONSE_make_json_pack ("{s:o, s:o, s:o, s:o, s:o}",
"reserve_pub",
GNUNET_JSON_from_data_auto (&reserve_pub),
@@ -452,6 +465,7 @@ MH_handler_tip_query (struct TMH_RequestHandler *rh,
}
tqc->reserve_priv = mi->tip_reserve;
+ db->preflight (db->cls);
{
int qs;
for (unsigned int i=0;i<MAX_RETRIES;i++)
diff --git a/src/backend/taler-merchant-httpd_track-transaction.c b/src/backend/taler-merchant-httpd_track-transaction.c
index 99bd33b4..b5e76ee9 100644
--- a/src/backend/taler-merchant-httpd_track-transaction.c
+++ b/src/backend/taler-merchant-httpd_track-transaction.c
@@ -248,7 +248,7 @@ struct TrackTransactionContext
* URL of the exchange we currently have in @e eh.
*/
const char *current_exchange;
-
+
/**
* Handle we use to resolve transactions for a given WTID.
*/
@@ -669,9 +669,9 @@ wtid_cb (void *cls,
/**
- * We have obtained all WTIDs, now prepare the response
+ * We have obtained all WTIDs, now prepare the response
*
- * @param tctx handle for the operation
+ * @param tctx handle for the operation
*/
static void
generate_response (struct TrackTransactionContext *tctx)
@@ -786,7 +786,7 @@ generate_response (struct TrackTransactionContext *tctx)
* Find the exchange to trace the next coin(s).
*
* @param tctx operation context
- */
+ */
static void
find_exchange (struct TrackTransactionContext *tctx);
@@ -973,7 +973,7 @@ coin_cb (void *cls,
* Find the exchange to trace the next coin(s).
*
* @param tctx operation context
- */
+ */
static void
find_exchange (struct TrackTransactionContext *tctx)
{
@@ -989,7 +989,7 @@ find_exchange (struct TrackTransactionContext *tctx)
NULL,
&process_track_transaction_with_exchange,
tctx);
-
+
}
else
{
@@ -1137,9 +1137,10 @@ MH_handler_track_transaction (struct TMH_RequestHandler *rh,
GNUNET_JSON_spec_end()
};
- if (GNUNET_YES != GNUNET_JSON_parse (contract_terms,
- spec,
- NULL, NULL))
+ if (GNUNET_YES !=
+ GNUNET_JSON_parse (contract_terms,
+ spec,
+ NULL, NULL))
{
GNUNET_break (0);
GNUNET_JSON_parse_free (spec);
@@ -1152,6 +1153,7 @@ MH_handler_track_transaction (struct TMH_RequestHandler *rh,
}
tctx->qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+ db->preflight (db->cls);
qs = db->find_payments (db->cls,
&tctx->h_contract_terms,
&tctx->mi->pubkey,
diff --git a/src/backend/taler-merchant-httpd_track-transfer.c b/src/backend/taler-merchant-httpd_track-transfer.c
index 197947fe..e22b51ee 100644
--- a/src/backend/taler-merchant-httpd_track-transfer.c
+++ b/src/backend/taler-merchant-httpd_track-transfer.c
@@ -211,7 +211,7 @@ hashmap_free (void *cls,
void *value)
{
struct TALER_Amount *amount = value;
-
+
GNUNET_free (amount);
return GNUNET_YES;
}
@@ -472,7 +472,7 @@ check_transfer (void *cls,
"exchange_deposit_proof", exchange_proof,
"conflict_offset", (json_int_t) rctx->current_offset,
"exchange_transfer_proof", rctx->original_response,
- "coin_pub", GNUNET_JSON_from_data_auto (coin_pub),
+ "coin_pub", GNUNET_JSON_from_data_auto (coin_pub),
"h_contract_terms", GNUNET_JSON_from_data_auto (&ttd->h_contract_terms),
"amount_with_fee", TALER_JSON_from_amount (amount_with_fee),
"deposit_fee", TALER_JSON_from_amount (deposit_fee));
@@ -483,7 +483,7 @@ check_transfer (void *cls,
/**
- * Check that the given @a wire_fee is what the
+ * Check that the given @a wire_fee is what the
* @a exchange_pub should charge at the @a execution_time.
* If the fee is correct (according to our database),
* return #GNUNET_OK. If we do not have the fee structure
@@ -546,7 +546,7 @@ check_wire_fee (struct TrackTransferContext *rctx,
if (0 <= TALER_amount_cmp (&expected_fee,
wire_fee))
return GNUNET_OK; /* expected_fee >= wire_fee */
-
+
/* Wire fee check failed, export proof to client */
resume_track_transfer_with_response
(rctx,
@@ -987,6 +987,7 @@ MH_handler_track_transfer (struct TMH_RequestHandler *rh,
}
/* Check if reply is already in database! */
+ db->preflight (db->cls);
qs = db->find_proof_by_wtid (db->cls,
rctx->url,
&rctx->wtid,
@@ -1000,7 +1001,7 @@ MH_handler_track_transfer (struct TMH_RequestHandler *rh,
GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
return TMH_RESPONSE_reply_internal_error (connection,
TALER_EC_TRACK_TRANSFER_DB_FETCH_FAILED,
- "Fail to query database about proofs");
+ "Fail to query database about proofs");
}
if (0 != rctx->response_code)
{
diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c
index 48b72448..96a5de9d 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2014, 2015, 2016, 2017 INRIA
+ (C) 2014-2018 INRIA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
@@ -51,6 +51,11 @@ struct PostgresClosure
*/
const struct GNUNET_CONFIGURATION_Handle *cfg;
+ /**
+ * Name of the currently active transaction, NULL if none is active.
+ */
+ const char *transaction_name;
+
};
@@ -700,10 +705,13 @@ check_connection (struct PostgresClosure *pg)
* Start a transaction.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param name unique name identifying the transaction (for debugging),
+ * must point to a constant
* @return #GNUNET_OK on success
*/
-int
-postgres_start (void *cls)
+static int
+postgres_start (void *cls,
+ const char *name)
{
struct PostgresClosure *pg = cls;
PGresult *result;
@@ -725,6 +733,7 @@ postgres_start (void *cls)
return GNUNET_SYSERR;
}
PQclear (result);
+ pg->transaction_name = name;
return GNUNET_OK;
}
@@ -748,6 +757,43 @@ postgres_rollback (void *cls)
GNUNET_break (PGRES_COMMAND_OK ==
PQresultStatus (result));
PQclear (result);
+ pg->transaction_name = NULL;
+}
+
+
+/**
+ * Do a pre-flight check that we are not in an uncommitted transaction.
+ * If we are, try to commit the previous transaction and output a warning.
+ * Does not return anything, as we will continue regardless of the outcome.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ */
+static void
+postgres_preflight (void *cls)
+{
+ struct PostgresClosure *pg = cls;
+ PGresult *result;
+ ExecStatusType status;
+
+ if (NULL == pg->transaction_name)
+ return; /* all good */
+ result = PQexec (pg->conn,
+ "COMMIT");
+ status = PQresultStatus (result);
+ if (PGRES_COMMAND_OK == status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "BUG: Preflight check committed transaction `%s'!\n",
+ pg->transaction_name);
+ }
+ else
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "BUG: Preflight check failed to commit transaction `%s'!\n",
+ pg->transaction_name);
+ }
+ pg->transaction_name = NULL;
+ PQclear (result);
}
@@ -767,6 +813,7 @@ postgres_commit (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Committing merchant DB transaction\n");
+ pg->transaction_name = NULL;
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"end_transaction",
params);
@@ -2181,7 +2228,6 @@ postgres_get_refunds_from_contract_terms_hash (void *cls,
TALER_LOG_DEBUG ("Looking for refund %s + %s\n",
GNUNET_h2s (h_contract_terms),
TALER_B2S (merchant_pub));
-
check_connection (pg);
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"find_refunds_from_contract_terms_hash",
@@ -2702,7 +2748,8 @@ postgres_increase_refund_for_contract (void *cls,
TALER_amount2s (refund),
GNUNET_h2s (h_contract_terms));
if (GNUNET_OK !=
- postgres_start (cls))
+ postgres_start (cls,
+ "increase refund"))
{
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
@@ -2835,7 +2882,8 @@ postgres_enable_tip_reserve (void *cls,
if (MAX_RETRIES < ++retries)
return GNUNET_DB_STATUS_SOFT_ERROR;
if (GNUNET_OK !=
- postgres_start (pg))
+ postgres_start (pg,
+ "enable tip reserve"))
{
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
@@ -3039,7 +3087,8 @@ postgres_authorize_tip (void *cls,
if (MAX_RETRIES < ++retries)
return TALER_EC_TIP_AUTHORIZE_DB_SOFT_ERROR;
if (GNUNET_OK !=
- postgres_start (pg))
+ postgres_start (pg,
+ "authorize tip"))
{
GNUNET_break (0);
return TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR;
@@ -3237,7 +3286,8 @@ postgres_pickup_tip (void *cls,
if (MAX_RETRIES < ++retries)
return TALER_EC_TIP_PICKUP_DB_ERROR_SOFT;
if (GNUNET_OK !=
- postgres_start (pg))
+ postgres_start (pg,
+ "pickup tip"))
{
GNUNET_break (0);
return TALER_EC_TIP_PICKUP_DB_ERROR_HARD;
@@ -3455,6 +3505,7 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
plugin->pickup_tip = &postgres_pickup_tip;
plugin->start = postgres_start;
plugin->commit = postgres_commit;
+ plugin->preflight = postgres_preflight;
plugin->rollback = postgres_rollback;
return plugin;
diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h
index 6f497b00..614d5dd7 100644
--- a/src/include/taler_merchantdb_plugin.h
+++ b/src/include/taler_merchantdb_plugin.h
@@ -758,10 +758,24 @@ struct TALER_MERCHANTDB_Plugin
* Start a transaction.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param name unique name identifying the transaction (for debugging),
+ * must point to a constant
* @return #GNUNET_OK on success
*/
int
- (*start) (void *cls);
+ (*start) (void *cls,
+ const char *name);
+
+
+ /**
+ * Do a pre-flight check that we are not in an uncommitted transaction.
+ * If we are, try to commit the previous transaction and output a warning.
+ * Does not return anything, as we will continue regardless of the outcome.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ */
+ void
+ (*preflight) (void *cls);
/**