From b65a2f77eb19b23063a5b73bc9ce5fb2e3622431 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 28 Nov 2020 18:56:29 +0100 Subject: sketch logic for wire-fee management endpoint --- .../taler-exchange-httpd_management_wire_fees.c | 276 +++++++++++++++++++++ src/include/taler_exchangedb_plugin.h | 41 ++- 2 files changed, 311 insertions(+), 6 deletions(-) create mode 100644 src/exchange/taler-exchange-httpd_management_wire_fees.c diff --git a/src/exchange/taler-exchange-httpd_management_wire_fees.c b/src/exchange/taler-exchange-httpd_management_wire_fees.c new file mode 100644 index 000000000..dca489c68 --- /dev/null +++ b/src/exchange/taler-exchange-httpd_management_wire_fees.c @@ -0,0 +1,276 @@ +/* + This file is part of TALER + Copyright (C) 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_management_wire_fees.c + * @brief Handle request to add wire fee details + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include +#include +#include +#include "taler_json_lib.h" +#include "taler_mhd_lib.h" +#include "taler-exchange-httpd_refund.h" +#include "taler-exchange-httpd_responses.h" +#include "taler-exchange-httpd_keystate.h" + +/** + * Closure for the #add_fee transaction. + */ +struct AddFeeContext +{ + /** + * Fee's signature affirming the #TALER_SIGNATURE_MASTER_WIRE_FEES operation. + */ + struct TALER_MasterSignatureP master_sig; + + /** + * Wire method this is about. + */ + const char *wire_method; + + /** + * Starting period. + */ + struct GNUNET_TIME_Absolute start_time; + + /** + * End of period. + */ + struct GNUNET_TIME_Absolute end_time; + + /** + * Wire fee amount. + */ + struct TALER_Amount wire_fee; + + /** + * Closing fee amount. + */ + struct TALER_Amount closing_fee; + +}; + + +/** + * Function implementing database transaction to add a fee. 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 closure with a `struct AddFeeContext` + * @param connection MHD request which triggered the transaction + * @param session database session to use + * @param[out] mhd_ret set to MHD response status for @a connection, + * if transaction failed (!) + * @return transaction status + */ +static enum GNUNET_DB_QueryStatus +add_fee (void *cls, + struct MHD_Connection *connection, + struct TALER_EXCHANGEDB_Session *session, + MHD_RESULT *mhd_ret) +{ + struct AddFeeContext *afc = cls; + enum GNUNET_DB_QueryStatus qs; + struct TALER_Amount wire_fee; + struct TALER_Amount closing_fee; + + qs = TEH_plugin->lookup_wire_fee ( + TEH_plugin->cls, + session, + aws->wire_method, + aws->start_time, + aws->end_time, + &wire_fee, + &closing_fee); + if (qs < 0) + { + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + return qs; + GNUNET_break (0); + *mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_LOOKUP_FAILED, + "lookup wire fee"); + return qs; + } + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs) + { + if ( (GNUNET_OK == + TALER_amount_is_valid (&wire_fee)) && + (0 == + TALER_amount_cmp (&wire_fee, + &afc->wire_fee)) && + (0 == + TALER_amount_cmp (&closing_fee, + &afc->closing_fee)) ) + { + /* this will trigger the 'success' response */ + return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; + } + else + { + *mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_CONFLICT, + TALER_EC_XXX, + NULL); + } + return GNUNET_DB_STATUS_HARD_ERROR; + } + + qs = TEH_plugin->insert_wire_fee ( + TEH_plugin->cls, + session, + aws->wire_method, + aws->start_time, + aws->end_time, + &aws->wire_fee, + &aws->closing_fee, + &aws->master_sig); + if (qs < 0) + { + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + return qs; + GNUNET_break (0); + *mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "insert fee"); + return qs; + } + return qs; +} + + +/** + * Handle a POST "/management/wire-fees" request. + * + * @param connection the MHD connection to handle + * @param root uploaded JSON data + * @return MHD result code + */ +MHD_RESULT +TEH_handler_management_post_wire_fees ( + struct MHD_Connection *connection, + const json_t *root) +{ + struct AddFeeContext afc; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("master_sig", + &afc.master_sig), + GNUNET_JSON_spec_string ("wire_method", + &afc.wire_method), + TALER_JSON_spec_time_abs ("fee_start", + &afc.start_time), + TALER_JSON_spec_time_abs ("fee_end", + &afc.end_time), + TALER_JSON_spec_amount ("closing_fee", + &afc.closing_fee), + TALER_JSON_spec_amount ("wire_fee", + &afc.wire_fee), + GNUNET_JSON_spec_end () + }; + enum GNUNET_DB_QueryStatus qs; + + { + enum GNUNET_GenericReturnValue res; + + res = TALER_MHD_parse_json_data (connection, + root, + spec); + if (GNUNET_SYSERR == res) + return MHD_NO; /* hard failure */ + if (GNUNET_NO == res) + return MHD_YES; /* failure */ + } + + if (0 != + TALER_amount_cmp_currency (&afc.closing_fee, + &afc.wire_fee)) + { + /* currencies of the two fees must be identical */ + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_BAD_CURRENCY, + NULL); + } + if (0 != + strcasecmp (afc.wire_fee.currency, + TEH_currency)) + { + /* currency does not match exchange's currency */ + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_PRECONDITION_FAILED, + TALER_EC_GENERIC_BAD_CURRENCY, + TEH_currency); + } + + { + struct TALER_MasterWireFeePS wf = { + .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_WIRE_FEES), + .purpose.size = htonl (wf), + .start_date = GNUNET_TIME_absolute_hton (afc.start_date), + .end_date = GNUNET_TIME_absolute_hton (afc.end_date), + }; + + TALER_amount_hton (&kv.wire_fee, + &afc.wire_fee); + TALER_amount_hton (&kv.closing_fee, + &afc.closing_fee); + GNUNET_CRYPTO_hash (afc.wire_method, + strlen (afc.wire_method) + 1, + &wf.h_wire_method); + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify ( + TALER_SIGNATURE_MASTER_WIRE_FEES, + &wf, + &afc.master_sig.eddsa_sig, + &TEH_master_public_key.eddsa_pub)) + { + /* signature invalid */ + GNUNET_break_op (0); + *mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_EXCHANGE_XXX, + NULL); + return GNUNET_DB_STATUS_HARD_ERROR; + } + } + + qs = TEH_DB_run_transaction (connection, + "add wire fee", + &res, + &add_fee, + &afc); + if (qs < 0) + return res; + return TALER_MHD_reply_static ( + connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0); +} + + +/* end of taler-exchange-httpd_management_wire_fees.c */ diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 159141f85..60ad0e65d 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -3172,12 +3172,41 @@ struct TALER_EXCHANGEDB_Plugin * @return transaction status code */ enum GNUNET_DB_QueryStatus - (*insert_auditor_denom_sig)(void *cls, - struct TALER_EXCHANGEDB_Session *session, - const struct GNUNET_HashCode *h_denom_pub, - const struct TALER_AuditorPublicKeyP *auditor_pub, - const struct - TALER_AuditorSignatureP *auditor_sig); + (*insert_auditor_denom_sig)( + void *cls, + struct TALER_EXCHANGEDB_Session *session, + const struct GNUNET_HashCode *h_denom_pub, + const struct TALER_AuditorPublicKeyP *auditor_pub, + const struct TALER_AuditorSignatureP *auditor_sig); + + + /** + * Lookup information about known wire fees. + * + * @param cls closure + * @param session a session + * @param wire_method the wire method to lookup fees for + * @param start_time starting time of fee + * @param end_time end time of fee + * @param[out] wire_fee wire fee for that time period; if + * different wire fee exists within this time + * period, an 'invalid' amount is returned. + * @param[out] closing_fee wire fee for that time period; if + * different wire fee exists within this time + * period, an 'invalid' amount is returned. + * @return transaction status code + */ + enum GNUNET_DB_QueryStatus + (*lookup_wire_fee_by_time)( + void *cls, + struct TALER_EXCHANGEDB_Session *session, + const char *wire_method, + struct GNUNET_TIME_Absolute start_time, + struct GNUNET_TIME_Absolute end_time, + struct TALER_Amount *wire_fee, + struct TALER_Amount *closing_fee); + + }; #endif /* _TALER_EXCHANGE_DB_H */ -- cgit v1.2.3