From 4a5d71cca2297cfd98b5dd907df2fc355d0da297 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 22 May 2022 16:18:09 +0200 Subject: -implement reserve history DB logic --- .../taler-exchange-httpd_reserves_history.c | 42 +++++++++++++++- src/exchangedb/exchange-0001-part.sql | 58 +++++++++++++++++++++- src/exchangedb/plugin_exchangedb_postgres.c | 35 +++++++++++-- src/include/taler_exchangedb_plugin.h | 9 +++- 4 files changed, 134 insertions(+), 10 deletions(-) diff --git a/src/exchange/taler-exchange-httpd_reserves_history.c b/src/exchange/taler-exchange-httpd_reserves_history.c index 96902d01c..4766dadc8 100644 --- a/src/exchange/taler-exchange-httpd_reserves_history.c +++ b/src/exchange/taler-exchange-httpd_reserves_history.c @@ -96,6 +96,8 @@ reply_reserve_history_success (struct MHD_Connection *connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE, NULL); + /* FIXME: should set explicit cache control headers + for this response to enable caching! */ return TALER_MHD_REPLY_JSON_PACK ( connection, MHD_HTTP_OK, @@ -129,7 +131,45 @@ reserve_history_transaction (void *cls, struct ReserveHistoryContext *rsc = cls; enum GNUNET_DB_QueryStatus qs; - // FIXME: first deduct rsc->gf->fees.history from reserve balance (and persist the signature justifying this) + if (! TALER_amount_is_zero (&rsc->gf->fees.history)) + { + bool balance_ok = false; + bool idempotent = true; + + qs = TEH_plugin->insert_history_request (TEH_plugin->cls, + rsc->reserve_pub, + &rsc->reserve_sig, + rsc->timestamp, + &rsc->gf->fees.history, + &balance_ok, + &idempotent); + if (GNUNET_DB_STATUS_HARD_ERROR == qs) + { + GNUNET_break (0); + *mhd_ret + = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "get_reserve_history"); + } + if (qs <= 0) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); + return qs; + } + if (! balance_ok) + { + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_CONFLICT, + TALER_EC_EXCHANGE_WITHDRAW_HISTORY_ERROR_INSUFFICIENT_FUNDS, + NULL); + } + if (idempotent) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Idempotent /reserves/history request observed. Is caching working?\n"); + } + } qs = TEH_plugin->get_reserve_history (TEH_plugin->cls, rsc->reserve_pub, &rsc->balance, diff --git a/src/exchangedb/exchange-0001-part.sql b/src/exchangedb/exchange-0001-part.sql index d8855b011..a47a2aede 100644 --- a/src/exchangedb/exchange-0001-part.sql +++ b/src/exchangedb/exchange-0001-part.sql @@ -3429,11 +3429,65 @@ CREATE OR REPLACE FUNCTION exchange_do_history_request( IN in_history_fee_val INT8, IN in_history_fee_frac INT4, OUT out_balance_ok BOOLEAN, - OUT out_conflict BOOLEAN) + OUT out_idempotent BOOLEAN) LANGUAGE plpgsql AS $$ BEGIN - -- FIXME + + -- Insert and check for idempotency. + INSERT INTO history_requests + (reserve_pub + ,request_timestamp + ,reserve_sig + ,history_fee_val + ,history_fee_frac) + VALUES + (in_reserve_pub + ,in_request_timestamp + ,in_reserve_sig + ,in_history_fee_val + ,in_history_fee_frac) + ON CONFLICT DO NOTHING; + + IF NOT FOUND + THEN + out_balance_ok=TRUE; + out_conflict=TRUE; + RETURN; + END IF; + + out_conflict=FALSE; + + -- Update reserve balance. + UPDATE reserves + SET + current_balance_frac=current_balance_frac-in_history_fee_frac + + CASE + WHEN reserve_frac < in_history_fee_frac + THEN 100000000 + ELSE 0 + END, + current_balance_val=current_balance_val-in_history_fee_val + - CASE + WHEN current_balance_frac < in_history_fee_frac + THEN 1 + ELSE 0 + END + WHERE + reserve_pub=in_reserve_pub + AND ( (current_balance_val > in_history_fee_val) OR + ( (current_balance_frac >= in_history_fee_frac) AND + (current_balance_val >= in_history_fee_val) ) ); + + IF NOT FOUND + THEN + -- Either reserve does not exist, or balance insufficient. + -- Both we treat the same here as balance insufficient. + out_balance_ok=FALSE; + RETURN; + END IF; + + out_balance_ok=TRUE; END $$; diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 7a908398d..5d84a2af4 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -3641,7 +3641,9 @@ prepare_statements (struct PostgresClosure *pg) /* Used in #postgres_insert_history_request() */ GNUNET_PQ_make_prepare ( "call_history_request", - "SELECT 1" + "SELECT" + " out_balance_ok AS balance_ok" + " ,out_idempotent AS idempotent" " FROM exchange_do_history_request" " ($1, $2, $3, $4, $5)", 5), @@ -14014,6 +14016,9 @@ postgres_do_account_merge ( * @param reserve_sig signature affirming the request * @param request_timestamp when was the request made * @param history_fee how much should the @a reserve_pub be charged for the request + * @param[out] balance_ok set to TRUE if the reserve balance + * was sufficient + * @param[out] idempotent set to TRUE if the request is already in the DB * @return transaction status code */ static enum GNUNET_DB_QueryStatus @@ -14021,11 +14026,31 @@ postgres_insert_history_request ( void *cls, const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_ReserveSignatureP *reserve_sig, - struct GNUNET_TIME_Absolute request_timestamp, - const struct TALER_Amount *history) + struct GNUNET_TIME_Timestamp request_timestamp, + const struct TALER_Amount *history, + bool *balance_ok, + bool *idempotent) { - GNUNET_break (0); // FIXME - return GNUNET_DB_STATUS_HARD_ERROR; + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (reserve_pub), + GNUNET_PQ_query_param_auto_from_type (reserve_sig), + GNUNET_PQ_query_param_timestamp (&request_timestamp), + TALER_PQ_query_param_amount (history), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_bool ("balance_ok", + balance_ok), + GNUNET_PQ_result_spec_bool ("idempotent", + idempotent), + GNUNET_PQ_result_spec_end + }; + + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "call_history_request", + params, + rs); } diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 213fe114d..2ef291235 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -4864,6 +4864,9 @@ struct TALER_EXCHANGEDB_Plugin * @param reserve_sig signature affirming the request * @param request_timestamp when was the request made * @param history_fee how much should the @a reserve_pub be charged for the request + * @param[out] balance_ok set to TRUE if the reserve balance + * was sufficient + * @param[out] idempotent set to TRUE if the request is already in the DB * @return transaction status code */ enum GNUNET_DB_QueryStatus @@ -4871,8 +4874,10 @@ struct TALER_EXCHANGEDB_Plugin void *cls, const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_ReserveSignatureP *reserve_sig, - struct GNUNET_TIME_Absolute request_timestamp, - const struct TALER_Amount *history_fee); + struct GNUNET_TIME_Timestamp request_timestamp, + const struct TALER_Amount *history_fee, + bool *balance_ok, + bool *idempotent); /** -- cgit v1.2.3