diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_check-payment.c | 8 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_exchanges.c | 4 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_history.c | 3 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_pay.c | 5 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_proposal.c | 4 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_refund.c | 2 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_tip-authorize.c | 1 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_tip-pickup.c | 1 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_tip-query.c | 44 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_track-transaction.c | 20 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_track-transfer.c | 11 | ||||
-rw-r--r-- | src/backenddb/plugin_merchantdb_postgres.c | 67 | ||||
-rw-r--r-- | src/include/taler_merchantdb_plugin.h | 16 |
14 files changed, 141 insertions, 46 deletions
@@ -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); /** |