From f5f15e6531bb7072a02cb976771a43803bd044f6 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 21 Mar 2022 02:39:36 +0100 Subject: [PATCH] first rough-cut implementation of POST /reserves//status --- contrib/gana | 2 +- src/exchange/Makefile.am | 1 + src/exchange/taler-exchange-httpd.c | 84 +++++++- src/exchange/taler-exchange-httpd_deposit.c | 12 -- src/exchange/taler-exchange-httpd_mhd.h | 2 +- .../taler-exchange-httpd_reserves_get.c | 86 ++++---- .../taler-exchange-httpd_reserves_status.c | 199 ++++++++++++++++++ .../taler-exchange-httpd_reserves_status.h | 43 ++++ src/exchange/taler-exchange-httpd_responses.c | 122 +---------- src/exchange/taler-exchange-httpd_responses.h | 7 +- src/exchange/taler-exchange-httpd_withdraw.c | 35 +-- src/exchange/taler-exchange-httpd_withdraw.h | 10 +- src/exchangedb/plugin_exchangedb_postgres.c | 43 ++++ src/include/taler_exchangedb_plugin.h | 14 ++ 14 files changed, 441 insertions(+), 219 deletions(-) create mode 100644 src/exchange/taler-exchange-httpd_reserves_status.c create mode 100644 src/exchange/taler-exchange-httpd_reserves_status.h diff --git a/contrib/gana b/contrib/gana index baeb82036..4cfefdf37 160000 --- a/contrib/gana +++ b/contrib/gana @@ -1 +1 @@ -Subproject commit baeb820366b88befd6f5aa2a551e2827ef406daf +Subproject commit 4cfefdf374de55fe9be3f0f039c7a13f496ab970 diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am index 3a07b6f46..2923aa2dc 100644 --- a/src/exchange/Makefile.am +++ b/src/exchange/Makefile.am @@ -108,6 +108,7 @@ taler_exchange_httpd_SOURCES = \ taler-exchange-httpd_refreshes_reveal.c taler-exchange-httpd_refreshes_reveal.h \ taler-exchange-httpd_refund.c taler-exchange-httpd_refund.h \ taler-exchange-httpd_reserves_get.c taler-exchange-httpd_reserves_get.h \ + taler-exchange-httpd_reserves_status.c taler-exchange-httpd_reserves_status.h \ taler-exchange-httpd_responses.c taler-exchange-httpd_responses.h \ taler-exchange-httpd_terms.c taler-exchange-httpd_terms.h \ taler-exchange-httpd_transfers_get.c taler-exchange-httpd_transfers_get.h \ diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index f0dc365a9..676135faf 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -48,6 +48,7 @@ #include "taler-exchange-httpd_refreshes_reveal.h" #include "taler-exchange-httpd_refund.h" #include "taler-exchange-httpd_reserves_get.h" +#include "taler-exchange-httpd_reserves_status.h" #include "taler-exchange-httpd_terms.h" #include "taler-exchange-httpd_transfers_get.h" #include "taler-exchange-httpd_wire.h" @@ -212,6 +213,19 @@ typedef MHD_RESULT const struct TALER_CoinSpendPublicKeyP *coin_pub, const json_t *root); +/** + * Signature of functions that handle operations on reserves. + * + * @param rc request context + * @param reserve_pub the public key of the reserve + * @param root uploaded JSON data + * @return MHD result code + */ +typedef MHD_RESULT +(*ReserveOpHandler)(struct TEH_RequestContext *rc, + const struct TALER_ReservePublicKeyP *reserve_pub, + const json_t *root); + /** * Generate a 404 "not found" reply on @a connection with @@ -237,8 +251,7 @@ r404 (struct MHD_Connection *connection, * * @param rc request context * @param root uploaded JSON data - * @param args array of additional options (first must be the - * reserve public key, the second one should be "withdraw") + * @param args array of additional options * @return MHD result code */ static MHD_RESULT @@ -309,6 +322,71 @@ handle_post_coins (struct TEH_RequestContext *rc, } +/** + * Handle a "/reserves/$RESERVE_PUB/$OP" POST request. Parses the "reserve_pub" + * EdDSA key of the reserve and demultiplexes based on $OP. + * + * @param rc request context + * @param root uploaded JSON data + * @param args array of additional options + * @return MHD result code + */ +static MHD_RESULT +handle_post_reserves (struct TEH_RequestContext *rc, + const json_t *root, + const char *const args[2]) +{ + struct TALER_ReservePublicKeyP reserve_pub; + static const struct + { + /** + * Name of the operation (args[1]) + */ + const char *op; + + /** + * Function to call to perform the operation. + */ + ReserveOpHandler handler; + + } h[] = { + { + .op = "withdraw", + .handler = &TEH_handler_withdraw + }, + { + .op = "status", + .handler = &TEH_handler_reserves_status + }, + { + .op = NULL, + .handler = NULL + }, + }; + + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data (args[0], + strlen (args[0]), + &reserve_pub, + sizeof (reserve_pub))) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_GENERIC_RESERVE_PUB_MALFORMED, + args[0]); + } + for (unsigned int i = 0; NULL != h[i].op; i++) + if (0 == strcmp (h[i].op, + args[1])) + return h[i].handler (rc, + &reserve_pub, + root); + return r404 (rc->connection, + args[1]); +} + + /** * Increments our request counter and checks if this * process should commit suicide. @@ -947,7 +1025,7 @@ handle_mhd_request (void *cls, { .url = "reserves", .method = MHD_HTTP_METHOD_POST, - .handler.post = &TEH_handler_withdraw, + .handler.post = &handle_post_reserves, .nargs = 2 }, /* coins */ diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c index a2e22f2e3..00353a401 100644 --- a/src/exchange/taler-exchange-httpd_deposit.c +++ b/src/exchange/taler-exchange-httpd_deposit.c @@ -205,18 +205,6 @@ deposit_transaction (void *cls, } -/** - * Handle a "/coins/$COIN_PUB/deposit" request. Parses the JSON, and, if - * successful, passes the JSON data to #deposit_transaction() to - * further check the details of the operation specified. If everything checks - * out, this will ultimately lead to the "/deposit" being executed, or - * rejected. - * - * @param connection the MHD connection to handle - * @param coin_pub public key of the coin - * @param root uploaded JSON data - * @return MHD result code - */ MHD_RESULT TEH_handler_deposit (struct MHD_Connection *connection, const struct TALER_CoinSpendPublicKeyP *coin_pub, diff --git a/src/exchange/taler-exchange-httpd_mhd.h b/src/exchange/taler-exchange-httpd_mhd.h index 270b0539a..563975beb 100644 --- a/src/exchange/taler-exchange-httpd_mhd.h +++ b/src/exchange/taler-exchange-httpd_mhd.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2020 Taler Systems SA + Copyright (C) 2014-2022 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software diff --git a/src/exchange/taler-exchange-httpd_reserves_get.c b/src/exchange/taler-exchange-httpd_reserves_get.c index 4b1bbddec..0b5db7c88 100644 --- a/src/exchange/taler-exchange-httpd_reserves_get.c +++ b/src/exchange/taler-exchange-httpd_reserves_get.c @@ -164,37 +164,6 @@ db_event_cb (void *cls, } -/** - * Send reserve history to client. - * - * @param connection connection to the client - * @param rh reserve history to return - * @return MHD result code - */ -static MHD_RESULT -reply_reserve_history_success (struct MHD_Connection *connection, - const struct TALER_EXCHANGEDB_ReserveHistory *rh) -{ - json_t *json_history; - struct TALER_Amount balance; - - json_history = TEH_RESPONSE_compile_reserve_history (rh, - &balance); - if (NULL == json_history) - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE, - NULL); - return TALER_MHD_REPLY_JSON_PACK ( - connection, - MHD_HTTP_OK, - TALER_JSON_pack_amount ("balance", - &balance), - GNUNET_JSON_pack_array_steal ("history", - json_history)); -} - - /** * Closure for #reserve_history_transaction. */ @@ -205,10 +174,18 @@ struct ReserveHistoryContext */ struct TALER_ReservePublicKeyP reserve_pub; +#ifndef MBOSS_DONE /** * History of the reserve, set in the callback. + * FIXME: get rid of this once benchmarking is done! */ struct TALER_EXCHANGEDB_ReserveHistory *rh; +#endif + + /** + * Balance of the reserve, set in the callback. + */ + struct TALER_Amount balance; }; @@ -226,23 +203,37 @@ struct ReserveHistoryContext * @param cls a `struct ReserveHistoryContext *` * @param connection MHD request which triggered the transaction * @param[out] mhd_ret set to MHD response status for @a connection, - * if transaction failed (!); unused + * if transaction failed (!) * @return transaction status */ static enum GNUNET_DB_QueryStatus -reserve_history_transaction (void *cls, +reserve_balance_transaction (void *cls, struct MHD_Connection *connection, MHD_RESULT *mhd_ret) { struct ReserveHistoryContext *rsc = cls; - struct TALER_Amount balance; + enum GNUNET_DB_QueryStatus qs; - (void) connection; - (void) mhd_ret; - return TEH_plugin->get_reserve_history (TEH_plugin->cls, - &rsc->reserve_pub, - &balance, - &rsc->rh); +#ifdef MBOSS_DONE + qs = TEH_plugin->get_reserve_balance (TEH_plugin->cls, + &rsc->reserve_pub, + &rsc->balance); +#else + qs = TEH_plugin->get_reserve_history (TEH_plugin->cls, + &rsc->reserve_pub, + &rsc->balance, + &rsc->rh); +#endif + 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_balance"); + } + return qs; } @@ -314,10 +305,10 @@ TEH_handler_reserves_get (struct TEH_RequestContext *rc, rsc.rh = NULL; if (GNUNET_OK != TEH_DB_run_transaction (rc->connection, - "get reserve history", + "get reserve balance", TEH_MT_REQUEST_OTHER, &mhd_ret, - &reserve_history_transaction, + &reserve_balance_transaction, &rsc)) { if (NULL != eh) @@ -335,7 +326,7 @@ TEH_handler_reserves_get (struct TEH_RequestContext *rc, { return TALER_MHD_reply_with_error (rc->connection, MHD_HTTP_NOT_FOUND, - TALER_EC_EXCHANGE_RESERVES_GET_STATUS_UNKNOWN, + TALER_EC_EXCHANGE_RESERVES_STATUS_UNKNOWN, args[0]); } GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -358,10 +349,15 @@ TEH_handler_reserves_get (struct TEH_RequestContext *rc, if (NULL != eh) TEH_plugin->event_listen_cancel (TEH_plugin->cls, eh); - mhd_ret = reply_reserve_history_success (rc->connection, - rsc.rh); + mhd_ret = TALER_MHD_REPLY_JSON_PACK ( + rc->connection, + MHD_HTTP_OK, + TALER_JSON_pack_amount ("balance", + &rsc.balance)); +#ifndef MBOSS_DONE TEH_plugin->free_reserve_history (TEH_plugin->cls, rsc.rh); +#endif return mhd_ret; } diff --git a/src/exchange/taler-exchange-httpd_reserves_status.c b/src/exchange/taler-exchange-httpd_reserves_status.c new file mode 100644 index 000000000..0b6ee2d30 --- /dev/null +++ b/src/exchange/taler-exchange-httpd_reserves_status.c @@ -0,0 +1,199 @@ +/* + This file is part of TALER + Copyright (C) 2014-2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see +*/ +/** + * @file taler-exchange-httpd_reserves_status.c + * @brief Handle /reserves/$RESERVE_PUB STATUS requests + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include "taler_mhd_lib.h" +#include "taler_json_lib.h" +#include "taler_dbevents.h" +#include "taler-exchange-httpd_keys.h" +#include "taler-exchange-httpd_reserves_status.h" +#include "taler-exchange-httpd_responses.h" + + +/** + * Closure for #reserve_status_transaction. + */ +struct ReserveStatusContext +{ + /** + * Public key of the reserve the inquiry is about. + */ + const struct TALER_ReservePublicKeyP *reserve_pub; + + /** + * History of the reserve, set in the callback. + */ + struct TALER_EXCHANGEDB_ReserveHistory *rh; + + /** + * Current reserve balance. + */ + struct TALER_Amount balance; +}; + + +/** + * Send reserve status to client. + * + * @param connection connection to the client + * @param rh reserve history to return + * @return MHD result code + */ +static MHD_RESULT +reply_reserve_status_success (struct MHD_Connection *connection, + const struct ReserveStatusContext *rhc) +{ + const struct TALER_EXCHANGEDB_ReserveHistory *rh = rhc->rh; + json_t *json_history; + + json_history = TEH_RESPONSE_compile_reserve_history (rh); + if (NULL == json_history) + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE, + NULL); + return TALER_MHD_REPLY_JSON_PACK ( + connection, + MHD_HTTP_OK, + TALER_JSON_pack_amount ("balance", + &rhc->balance), + GNUNET_JSON_pack_array_steal ("history", + json_history)); +} + + +/** + * Function implementing /reserves/ STATUS transaction. + * Execute a /reserves/ STATUS. Given the public key of a reserve, + * return the associated transaction history. Runs the + * transaction logic; IF it returns a non-error code, the transaction + * logic MUST NOT queue a MHD response. IF it returns an hard error, + * the transaction logic MUST queue a MHD response and set @a mhd_ret. + * IF it returns the soft error code, the function MAY be called again + * to retry and MUST not queue a MHD response. + * + * @param cls a `struct ReserveStatusContext *` + * @param connection MHD request which triggered the transaction + * @param[out] mhd_ret set to MHD response status for @a connection, + * if transaction failed (!); unused + * @return transaction status + */ +static enum GNUNET_DB_QueryStatus +reserve_status_transaction (void *cls, + struct MHD_Connection *connection, + MHD_RESULT *mhd_ret) +{ + struct ReserveStatusContext *rsc = cls; + enum GNUNET_DB_QueryStatus qs; + + qs = TEH_plugin->get_reserve_history (TEH_plugin->cls, + rsc->reserve_pub, + &rsc->balance, + &rsc->rh); + 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_status"); + } + return qs; +} + + +MHD_RESULT +TEH_handler_reserves_status (struct TEH_RequestContext *rc, + const struct TALER_ReservePublicKeyP *reserve_pub, + const json_t *root) +{ + struct ReserveStatusContext rsc; + MHD_RESULT mhd_ret; + struct GNUNET_TIME_Timestamp timestamp; + struct TALER_ReserveSignatureP reserve_sig; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_timestamp ("request_timestamp", + ×tamp), + GNUNET_JSON_spec_fixed_auto ("reserve_sig", + &reserve_sig), + GNUNET_JSON_spec_end () + }; + + rsc.reserve_pub = reserve_pub; + { + enum GNUNET_GenericReturnValue res; + + res = TALER_MHD_parse_json_data (rc->connection, + root, + spec); + if (GNUNET_SYSERR == res) + { + GNUNET_break (0); + return MHD_NO; /* hard failure */ + } + if (GNUNET_NO == res) + { + GNUNET_break_op (0); + return MHD_YES; /* failure */ + } + } + if (GNUNET_OK != + TALER_wallet_reserve_status_verify (timestamp, + reserve_pub, + &reserve_sig)) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_EXCHANGE_RESERVES_STATUS_BAD_SIGNATURE, + NULL); + } + rsc.rh = NULL; + if (GNUNET_OK != + TEH_DB_run_transaction (rc->connection, + "get reserve status", + TEH_MT_REQUEST_OTHER, + &mhd_ret, + &reserve_status_transaction, + &rsc)) + { + return mhd_ret; + } + if (NULL == rsc.rh) + { + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_RESERVES_STATUS_UNKNOWN, + NULL); + } + mhd_ret = reply_reserve_status_success (rc->connection, + &rsc); + TEH_plugin->free_reserve_history (TEH_plugin->cls, + rsc.rh); + return mhd_ret; +} + + +/* end of taler-exchange-httpd_reserves_status.c */ diff --git a/src/exchange/taler-exchange-httpd_reserves_status.h b/src/exchange/taler-exchange-httpd_reserves_status.h new file mode 100644 index 000000000..831b270f7 --- /dev/null +++ b/src/exchange/taler-exchange-httpd_reserves_status.h @@ -0,0 +1,43 @@ +/* + This file is part of TALER + Copyright (C) 2014-2020 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see +*/ +/** + * @file taler-exchange-httpd_reserves_status.h + * @brief Handle /reserves/$RESERVE_PUB STATUS requests + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#ifndef TALER_EXCHANGE_HTTPD_RESERVES_STATUS_H +#define TALER_EXCHANGE_HTTPD_RESERVES_STATUS_H + +#include +#include "taler-exchange-httpd.h" + + +/** + * Handle a POST "/reserves/$RID/status" request. + * + * @param rc request context + * @param reserve_pub public key of the reserve + * @param root uploaded body from the client + * @return MHD result code + */ +MHD_RESULT +TEH_handler_reserves_status (struct TEH_RequestContext *rc, + const struct TALER_ReservePublicKeyP *reserve_pub, + const json_t *root); + +#endif diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c index f1a219fc8..ee8c902dd 100644 --- a/src/exchange/taler-exchange-httpd_responses.c +++ b/src/exchange/taler-exchange-httpd_responses.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2021 Taler Systems SA + Copyright (C) 2014-2022 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -605,31 +605,11 @@ TEH_RESPONSE_reply_coin_insufficient_funds ( } -/** - * Compile the history of a reserve into a JSON object - * and calculate the total balance. - * - * @param rh reserve history to JSON-ify - * @param[out] balance set to current reserve balance - * @return json representation of the @a rh, NULL on error - */ json_t * TEH_RESPONSE_compile_reserve_history ( - const struct TALER_EXCHANGEDB_ReserveHistory *rh, - struct TALER_Amount *balance) + const struct TALER_EXCHANGEDB_ReserveHistory *rh) { - struct TALER_Amount credit_total; - struct TALER_Amount withdraw_total; json_t *json_history; - enum InitAmounts - { - /** Nothing initialized */ - IA_NONE = 0, - /** credit_total initialized */ - IA_CREDIT = 1, - /** withdraw_total initialized */ - IA_WITHDRAW = 2 - } init = IA_NONE; json_history = json_array (); for (const struct TALER_EXCHANGEDB_ReserveHistory *pos = rh; @@ -642,20 +622,7 @@ TEH_RESPONSE_compile_reserve_history ( { const struct TALER_EXCHANGEDB_BankTransfer *bank = pos->details.bank; - if (0 == (IA_CREDIT & init)) - { - credit_total = bank->amount; - init |= IA_CREDIT; - } - else if (0 > - TALER_amount_add (&credit_total, - &credit_total, - &bank->amount)) - { - GNUNET_break (0); - json_decref (json_history); - return NULL; - } + if (0 != json_array_append_new ( json_history, @@ -681,26 +648,7 @@ TEH_RESPONSE_compile_reserve_history ( { const struct TALER_EXCHANGEDB_CollectableBlindcoin *withdraw = pos->details.withdraw; - struct TALER_Amount value; - value = withdraw->amount_with_fee; - if (0 == (IA_WITHDRAW & init)) - { - withdraw_total = value; - init |= IA_WITHDRAW; - } - else - { - if (0 > - TALER_amount_add (&withdraw_total, - &withdraw_total, - &value)) - { - GNUNET_break (0); - json_decref (json_history); - return NULL; - } - } if (0 != json_array_append_new ( json_history, @@ -716,7 +664,7 @@ TEH_RESPONSE_compile_reserve_history ( TALER_JSON_pack_amount ("withdraw_fee", &withdraw->withdraw_fee), TALER_JSON_pack_amount ("amount", - &value)))) + &withdraw->amount_with_fee)))) { GNUNET_break (0); json_decref (json_history); @@ -731,20 +679,6 @@ TEH_RESPONSE_compile_reserve_history ( struct TALER_ExchangePublicKeyP pub; struct TALER_ExchangeSignatureP sig; - if (0 == (IA_CREDIT & init)) - { - credit_total = recoup->value; - init |= IA_CREDIT; - } - else if (0 > - TALER_amount_add (&credit_total, - &credit_total, - &recoup->value)) - { - GNUNET_break (0); - json_decref (json_history); - return NULL; - } { struct TALER_RecoupConfirmationPS pc = { .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP), @@ -796,26 +730,7 @@ TEH_RESPONSE_compile_reserve_history ( pos->details.closing; struct TALER_ExchangePublicKeyP pub; struct TALER_ExchangeSignatureP sig; - struct TALER_Amount value; - value = closing->amount; - if (0 == (IA_WITHDRAW & init)) - { - withdraw_total = value; - init |= IA_WITHDRAW; - } - else - { - if (0 > - TALER_amount_add (&withdraw_total, - &withdraw_total, - &value)) - { - GNUNET_break (0); - json_decref (json_history); - return NULL; - } - } { struct TALER_ReserveCloseConfirmationPS rcc = { .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED), @@ -826,7 +741,7 @@ TEH_RESPONSE_compile_reserve_history ( }; TALER_amount_hton (&rcc.closing_amount, - &value); + &closing->amount); TALER_amount_hton (&rcc.closing_fee, &closing->closing_fee); TALER_payto_hash (closing->receiver_account_details, @@ -858,7 +773,7 @@ TEH_RESPONSE_compile_reserve_history ( GNUNET_JSON_pack_timestamp ("timestamp", closing->execution_date), TALER_JSON_pack_amount ("amount", - &value), + &closing->amount), TALER_JSON_pack_amount ("closing_fee", &closing->closing_fee)))) { @@ -871,31 +786,6 @@ TEH_RESPONSE_compile_reserve_history ( } } - if (0 == (IA_CREDIT & init)) - { - /* We should not have gotten here, without credits no reserve - should exist! */ - GNUNET_break (0); - json_decref (json_history); - return NULL; - } - if (0 == (IA_WITHDRAW & init)) - { - /* did not encounter any withdraw operations, set withdraw_total to zero */ - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (credit_total.currency, - &withdraw_total)); - } - if (0 > - TALER_amount_subtract (balance, - &credit_total, - &withdraw_total)) - { - GNUNET_break (0); - json_decref (json_history); - return NULL; - } - return json_history; } diff --git a/src/exchange/taler-exchange-httpd_responses.h b/src/exchange/taler-exchange-httpd_responses.h index ffd2cc9e1..03841e801 100644 --- a/src/exchange/taler-exchange-httpd_responses.h +++ b/src/exchange/taler-exchange-httpd_responses.h @@ -34,17 +34,14 @@ /** - * Compile the history of a reserve into a JSON object - * and calculate the total balance. + * Compile the history of a reserve into a JSON object. * * @param rh reserve history to JSON-ify - * @param[out] balance set to current reserve balance * @return json representation of the @a rh, NULL on error */ json_t * TEH_RESPONSE_compile_reserve_history ( - const struct TALER_EXCHANGEDB_ReserveHistory *rh, - struct TALER_Amount *balance); + const struct TALER_EXCHANGEDB_ReserveHistory *rh); /** diff --git a/src/exchange/taler-exchange-httpd_withdraw.c b/src/exchange/taler-exchange-httpd_withdraw.c index fcf596e06..5765181b2 100644 --- a/src/exchange/taler-exchange-httpd_withdraw.c +++ b/src/exchange/taler-exchange-httpd_withdraw.c @@ -52,32 +52,19 @@ reply_withdraw_insufficient_funds ( const struct TALER_EXCHANGEDB_ReserveHistory *rh) { json_t *json_history; - struct TALER_Amount balance; - json_history = TEH_RESPONSE_compile_reserve_history (rh, - &balance); + json_history = TEH_RESPONSE_compile_reserve_history (rh); if (NULL == json_history) return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_EXCHANGE_WITHDRAW_HISTORY_ERROR_INSUFFICIENT_FUNDS, NULL); - if (0 != - TALER_amount_cmp (&balance, - ebalance)) - { - GNUNET_break (0); - json_decref (json_history); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_INVARIANT_FAILURE, - "reserve balance corrupt"); - } return TALER_MHD_REPLY_JSON_PACK ( connection, MHD_HTTP_CONFLICT, TALER_JSON_pack_ec (TALER_EC_EXCHANGE_WITHDRAW_INSUFFICIENT_FUNDS), TALER_JSON_pack_amount ("balance", - &balance), + ebalance), TALER_JSON_pack_amount ("requested_amount", withdraw_amount), GNUNET_JSON_pack_array_steal ("history", @@ -105,7 +92,6 @@ struct WithdrawContext */ struct TALER_Amount amount_with_fee; - /** * Blinded planchet. */ @@ -329,8 +315,8 @@ check_request_idempotent (struct TEH_RequestContext *rc, MHD_RESULT TEH_handler_withdraw (struct TEH_RequestContext *rc, - const json_t *root, - const char *const args[2]) + const struct TALER_ReservePublicKeyP *reserve_pub, + const json_t *root) { struct WithdrawContext wc; struct GNUNET_JSON_Specification spec[] = { @@ -348,18 +334,7 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc, memset (&wc, 0, sizeof (wc)); - if (GNUNET_OK != - GNUNET_STRINGS_string_to_data (args[0], - strlen (args[0]), - &wc.collectable.reserve_pub, - sizeof (wc.collectable.reserve_pub))) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_MERCHANT_GENERIC_RESERVE_PUB_MALFORMED, - args[0]); - } + wc.collectable.reserve_pub = *reserve_pub; { enum GNUNET_GenericReturnValue res; diff --git a/src/exchange/taler-exchange-httpd_withdraw.h b/src/exchange/taler-exchange-httpd_withdraw.h index 8d2d8c182..b754e64fd 100644 --- a/src/exchange/taler-exchange-httpd_withdraw.h +++ b/src/exchange/taler-exchange-httpd_withdraw.h @@ -28,8 +28,7 @@ /** - * Handle a "/reserves/$RESERVE_PUB/withdraw" request. Parses the - * "reserve_pub" EdDSA key of the reserve and the requested "denom_pub" which + * Handle a "/reserves/$RESERVE_PUB/withdraw" request. Parses the the requested "denom_pub" which * specifies the key/value of the coin to be withdrawn, and checks that the * signature "reserve_sig" makes this a valid withdrawal request from the * specified reserve. If so, the envelope with the blinded coin "coin_ev" is @@ -37,13 +36,12 @@ * * @param rc request context * @param root uploaded JSON data - * @param args array of additional options (first must be the - * reserve public key, the second one should be "withdraw") + * @param reserve_pub public key of the reserve * @return MHD result code */ MHD_RESULT TEH_handler_withdraw (struct TEH_RequestContext *rc, - const json_t *root, - const char *const args[2]); + const struct TALER_ReservePublicKeyP *reserve_pub, + const json_t *root); #endif diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index bb6f46f59..2856f3009 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -732,6 +732,17 @@ prepare_statements (struct PostgresClosure *pg) " WHERE res.reserve_pub=$1;", 1), /* Used in #postgres_select_withdrawals_above_serial_id() */ + + GNUNET_PQ_make_prepare ( + "get_reserve_balance", + "SELECT" + " current_balance_val" + ",current_balance_frac" + " FROM reserves" + " WHERE reserve_pub=$1;", + 1), + /* Fetch deposits with rowid '\geq' the given parameter */ + GNUNET_PQ_make_prepare ( "audit_get_reserves_out_incr", "SELECT" @@ -5533,6 +5544,37 @@ postgres_get_reserve_history (void *cls, } +/** + * Get the balance of the specified reserve. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param reserve_pub public key of the reserve + * @param[out] balance set to the reserve balance + * @return transaction status + */ +static enum GNUNET_DB_QueryStatus +postgres_get_reserve_balance (void *cls, + const struct TALER_ReservePublicKeyP *reserve_pub, + struct TALER_Amount *balance) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (reserve_pub), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + TALER_PQ_RESULT_SPEC_AMOUNT ("current_balance", + balance), + GNUNET_PQ_result_spec_end + }; + + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "get_reserve_balance", + params, + rs); +} + + /** * Check if we have the specified deposit already in the database. * @@ -12503,6 +12545,7 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) plugin->do_refund = &postgres_do_refund; plugin->do_recoup = &postgres_do_recoup; plugin->do_recoup_refresh = &postgres_do_recoup_refresh; + plugin->get_reserve_balance = &postgres_get_reserve_balance; plugin->get_reserve_history = &postgres_get_reserve_history; plugin->free_reserve_history = &common_free_reserve_history; plugin->count_known_coins = &postgres_count_known_coins; diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index fc909a1ba..610f20303 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -2846,6 +2846,20 @@ struct TALER_EXCHANGEDB_Plugin struct TALER_EXCHANGEDB_ReserveHistory **rhp); + /** + * The current reserve balance of the specified reserve. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param reserve_pub public key of the reserve + * @param[out] balance set to the reserve balance + * @return transaction status + */ + enum GNUNET_DB_QueryStatus + (*get_reserve_balance)(void *cls, + const struct TALER_ReservePublicKeyP *reserve_pub, + struct TALER_Amount *balance); + + /** * Free memory associated with the given reserve history. *