From fa5582930e6198e2783ae9cad59d2d2b4ce2ee1a Mon Sep 17 00:00:00 2001 From: Özgür Kesim Date: Thu, 21 Dec 2023 23:57:23 +0100 Subject: [wip, #7267] more refined denomination conflict response during deposit If a coin was known but with a different denomination, return to the client an error response that contains the denomination's public key and the corresponding signature of the provided coin. --- src/exchange/taler-exchange-httpd_db.c | 43 +++++++++++++--- src/exchange/taler-exchange-httpd_responses.c | 30 +++++++++-- src/exchange/taler-exchange-httpd_responses.h | 22 +++++++++ src/exchangedb/Makefile.am | 1 + src/exchangedb/pg_get_signature_for_known_coin.c | 63 ++++++++++++++++++++++++ src/exchangedb/pg_get_signature_for_known_coin.h | 43 ++++++++++++++++ src/exchangedb/plugin_exchangedb_postgres.c | 19 ++++--- src/include/taler_exchangedb_plugin.h | 18 ++++++- 8 files changed, 218 insertions(+), 21 deletions(-) create mode 100644 src/exchangedb/pg_get_signature_for_known_coin.c create mode 100644 src/exchangedb/pg_get_signature_for_known_coin.h diff --git a/src/exchange/taler-exchange-httpd_db.c b/src/exchange/taler-exchange-httpd_db.c index 5be12a508..1c11a02fb 100644 --- a/src/exchange/taler-exchange-httpd_db.c +++ b/src/exchange/taler-exchange-httpd_db.c @@ -19,9 +19,11 @@ * @author Christian Grothoff */ #include "platform.h" +#include #include #include #include +#include "taler_error_codes.h" #include "taler_exchangedb_plugin.h" #include "taler_json_lib.h" #include "taler_mhd_lib.h" @@ -62,14 +64,39 @@ TEH_make_coin_known (const struct TALER_CoinPublicInfo *coin, NULL); return GNUNET_DB_STATUS_HARD_ERROR; case TALER_EXCHANGEDB_CKS_DENOM_CONFLICT: - /* FIXME: insufficient_funds != denom conflict! See issue #7267, need new - * strategy for evidence gathering */ - *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds ( - connection, - TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY, - &h_denom_pub, - &coin->coin_pub); - return GNUNET_DB_STATUS_HARD_ERROR; + /* The exchange has a seen this coin before, but with a different denomination. + * Get the corresponding signature and sent it to the client as proof */ + { + struct conflict + { + struct TALER_DenominationPublicKey pub; + struct TALER_DenominationSignature sig; + } conflict = {0}; + + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + TEH_plugin->get_signature_for_known_coin (TEH_plugin->cls, + &coin->coin_pub, + &conflict.pub, + &conflict.sig)) + { + /* There _should_ have been a result, because + * we ended here due to a conflict! */ + *mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL); + return GNUNET_DB_STATUS_HARD_ERROR; + } + + *mhd_ret = TEH_RESPONSE_reply_coin_denomination_conflict ( + connection, + TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY, + &coin->coin_pub, + &conflict.pub, + &conflict.sig); + + return GNUNET_DB_STATUS_HARD_ERROR; + } case TALER_EXCHANGEDB_CKS_AGE_CONFLICT_EXPECTED_NULL: case TALER_EXCHANGEDB_CKS_AGE_CONFLICT_EXPECTED_NON_NULL: case TALER_EXCHANGEDB_CKS_AGE_CONFLICT_VALUE_DIFFERS: diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c index 2d8dede51..a6d2c7ffc 100644 --- a/src/exchange/taler-exchange-httpd_responses.c +++ b/src/exchange/taler-exchange-httpd_responses.c @@ -179,6 +179,29 @@ TEH_RESPONSE_reply_coin_insufficient_funds ( } +MHD_RESULT +TEH_RESPONSE_reply_coin_denomination_conflict ( + struct MHD_Connection *connection, + enum TALER_ErrorCode ec, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_DenominationPublicKey *prev_denom_pub, + const struct TALER_DenominationSignature *prev_denom_sig) +{ + return TALER_MHD_REPLY_JSON_PACK ( + connection, + TALER_ErrorCode_get_http_status_safe (ec), + TALER_JSON_pack_ec (ec), + GNUNET_JSON_pack_data_auto ("coin_pub", + coin_pub), + TALER_JSON_pack_denom_pub ("prev_denom_pub", + prev_denom_pub), + TALER_JSON_pack_denom_sig ("prev_denom_sig", + prev_denom_sig) + ); + +} + + MHD_RESULT TEH_RESPONSE_reply_coin_age_commitment_conflict ( struct MHD_Connection *connection, @@ -188,7 +211,6 @@ TEH_RESPONSE_reply_coin_age_commitment_conflict ( const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_AgeCommitmentHash *h_age_commitment) { - const struct TALER_AgeCommitmentHash *hac = h_age_commitment; const char *conflict_detail; switch (status) @@ -196,10 +218,10 @@ TEH_RESPONSE_reply_coin_age_commitment_conflict ( case TALER_EXCHANGEDB_CKS_AGE_CONFLICT_EXPECTED_NULL: conflict_detail = "expected NULL age commitment hash"; - hac = NULL; + h_age_commitment = NULL; break; case TALER_EXCHANGEDB_CKS_AGE_CONFLICT_EXPECTED_NON_NULL: - conflict_detail = "unexpected NULL age commitment hash"; + conflict_detail = "expected non-NULL age commitment hash"; break; case TALER_EXCHANGEDB_CKS_AGE_CONFLICT_VALUE_DIFFERS: conflict_detail = "expected age commitment hash differs"; @@ -218,7 +240,7 @@ TEH_RESPONSE_reply_coin_age_commitment_conflict ( h_denom_pub), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_data_auto ("expected_age_commitment_hash", - hac)), + h_age_commitment)), GNUNET_JSON_pack_string ("conflict_detail", conflict_detail) ); diff --git a/src/exchange/taler-exchange-httpd_responses.h b/src/exchange/taler-exchange-httpd_responses.h index b10a72824..57aeefe42 100644 --- a/src/exchange/taler-exchange-httpd_responses.h +++ b/src/exchange/taler-exchange-httpd_responses.h @@ -160,6 +160,28 @@ TEH_RESPONSE_reply_coin_insufficient_funds ( const struct TALER_DenominationHashP *h_denom_pub, const struct TALER_CoinSpendPublicKeyP *coin_pub); +/** + * Send proof that a request is invalid to client because of + * an conflict with the provided denomination (the exchange had seen + * this coin before, signed by a different denomination). + * This function will create a message with the denomination's public key + * that was seen before. + * + * @param connection connection to the client + * @param ec error code to return + * @param coin_pub the public key of the coin + * @param prev_denom_pub the denomination of the coin, as seen previously + * @param prev_denom_sig the signature with the denomination key over the coin + * @return MHD result code + */ +MHD_RESULT +TEH_RESPONSE_reply_coin_denomination_conflict ( + struct MHD_Connection *connection, + enum TALER_ErrorCode ec, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_DenominationPublicKey *prev_denom_pub, + const struct TALER_DenominationSignature *prev_denom_sig); + /** * Send proof that a request is invalid to client because of * a conflicting value for the age commitment hash of a coin. diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am index 84bd53018..847f2d880 100644 --- a/src/exchangedb/Makefile.am +++ b/src/exchangedb/Makefile.am @@ -126,6 +126,7 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \ pg_count_known_coins.h pg_count_known_coins.c \ pg_ensure_coin_known.h pg_ensure_coin_known.c \ pg_get_known_coin.h pg_get_known_coin.c \ + pg_get_signature_for_known_coin.h pg_get_signature_for_known_coin.c \ pg_get_coin_denomination.h pg_get_coin_denomination.c \ pg_have_deposit2.h pg_have_deposit2.c \ pg_aggregate.h pg_aggregate.c \ diff --git a/src/exchangedb/pg_get_signature_for_known_coin.c b/src/exchangedb/pg_get_signature_for_known_coin.c new file mode 100644 index 000000000..06074312f --- /dev/null +++ b/src/exchangedb/pg_get_signature_for_known_coin.c @@ -0,0 +1,63 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see + */ +/** + * @file exchangedb/pg_get_signature_for_known_coin.c + * @brief Implementation of the get_signature_for_known_coin function for Postgres + * @author Özgür Kesim + */ +#include "platform.h" +#include "taler_error_codes.h" +#include "taler_dbevents.h" +#include "taler_pq_lib.h" +#include "pg_get_signature_for_known_coin.h" +#include "pg_helper.h" + +enum GNUNET_DB_QueryStatus +TEH_PG_get_signature_for_known_coin ( + void *cls, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + struct TALER_DenominationPublicKey *denom_pub, + struct TALER_DenominationSignature *denom_sig) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (coin_pub), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + TALER_PQ_result_spec_denom_pub ("denom_pub", + denom_pub), + TALER_PQ_result_spec_denom_sig ("denom_sig", + denom_sig), + GNUNET_PQ_result_spec_end + }; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Getting denomination and signature for (potentially) known coin %s\n", + TALER_B2S (coin_pub)); + PREPARE (pg, + "get_signature_for_known_coin", + "SELECT" + " denominations.denom_pub" + ",denom_sig" + " FROM known_coins" + " JOIN denominations USING (denominations_serial)" + " WHERE coin_pub=$1;"); + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "get_signature_for_known_coin", + params, + rs); +} diff --git a/src/exchangedb/pg_get_signature_for_known_coin.h b/src/exchangedb/pg_get_signature_for_known_coin.h new file mode 100644 index 000000000..ec389176b --- /dev/null +++ b/src/exchangedb/pg_get_signature_for_known_coin.h @@ -0,0 +1,43 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see + */ +/** + * @file exchangedb/pg_get_signature_for_known_coin.h + * @brief implementation of the get_signature_for_known_coin function for Postgres + * @author Özgür Kesim + */ +#ifndef PG_GET_SIGNATURE_FOR_KNOWN_COIN_H +#define PG_GET_SIGNATURE_FOR_KNOWN_COIN_H + +#include "taler_util.h" +#include "taler_json_lib.h" +#include "taler_exchangedb_plugin.h" +/** + * Retrieve the denomination and the corresponding signature for a known coin. + * + * @param cls the plugin closure + * @param coin_pub the public key of the coin to search for + * @param[out] denom_pub the denomination of the public key, if coin was present + * @param[out] denom_sig the signature with the denomination key of the coin, if coin was present + * @return transaction status code + */ +enum GNUNET_DB_QueryStatus +TEH_PG_get_signature_for_known_coin ( + void *cls, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + struct TALER_DenominationPublicKey *denom_pub, + struct TALER_DenominationSignature *denom_sig); + +#endif diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 51dbea659..7d9044a1e 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -130,6 +130,7 @@ #include "pg_count_known_coins.h" #include "pg_ensure_coin_known.h" #include "pg_get_known_coin.h" +#include "pg_get_signature_for_known_coin.h" #include "pg_get_coin_denomination.h" #include "pg_have_deposit2.h" #include "pg_aggregate.h" @@ -232,14 +233,14 @@ * @param conn SQL connection that was used */ #define BREAK_DB_ERR(result,conn) do { \ - GNUNET_break (0); \ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, \ - "Database failure: %s/%s/%s/%s/%s", \ - PQresultErrorField (result, PG_DIAG_MESSAGE_PRIMARY), \ - PQresultErrorField (result, PG_DIAG_MESSAGE_DETAIL), \ - PQresultErrorMessage (result), \ - PQresStatus (PQresultStatus (result)), \ - PQerrorMessage (conn)); \ + GNUNET_break (0); \ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, \ + "Database failure: %s/%s/%s/%s/%s", \ + PQresultErrorField (result, PG_DIAG_MESSAGE_PRIMARY), \ + PQresultErrorField (result, PG_DIAG_MESSAGE_DETAIL), \ + PQresultErrorMessage (result), \ + PQresStatus (PQresultStatus (result)), \ + PQerrorMessage (conn)); \ } while (0) @@ -601,6 +602,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) = &TEH_PG_ensure_coin_known; plugin->get_known_coin = &TEH_PG_get_known_coin; + plugin->get_signature_for_known_coin + = &TEH_PG_get_signature_for_known_coin; plugin->get_coin_denomination = &TEH_PG_get_coin_denomination; plugin->have_deposit2 diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 596764b1c..25d3b17f9 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -4358,7 +4358,8 @@ struct TALER_EXCHANGEDB_Plugin * Retrieve information about the given @a coin from the database. * * @param cls database connection plugin state - * @param coin the coin that must be made known + * @param coin_pub the coin that must be made known + * @param[out] coin_info detailed information about the coin * @return database transaction status, non-negative on success */ enum GNUNET_DB_QueryStatus @@ -4366,6 +4367,21 @@ struct TALER_EXCHANGEDB_Plugin const struct TALER_CoinSpendPublicKeyP *coin_pub, struct TALER_CoinPublicInfo *coin_info); + /** + * Retrieve the signature and corresponding denomination for a given @a coin + * from the database + * + * @param cls database connection plugin state + * @param coin_pub the public key of the coin we search for + * @param[out] denom_pub the public key of the denomination that the coin was signed with + * @param[out] denom_sig the signature with the denomination's private key over the coin_pub + */ + enum GNUNET_DB_QueryStatus + (*get_signature_for_known_coin) ( + void *cls, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + struct TALER_DenominationPublicKey *denom_pub, + struct TALER_DenominationSignature *denom_sig); /** * Retrieve the denomination of a known coin. -- cgit v1.2.3