exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

commit b7f27ea64b64e2cf90ad959f77a0ab791375b0d7
parent b6594e0b1660e27ad2bdf41effb306d5cf51d2d2
Author: Özgür Kesim <oec@codeblau.de>
Date:   Tue,  6 May 2025 17:03:16 +0200

Pre-v27 refresh protocol and batch-withdraw removed

The refresh protocol prior to v27 has been removed, from
the REST-API (endpoints /coin/$COIN/{melt,reveal}), the
C-API and the database plugin.

Also, the endpoint for /batch-withdraw has been removed, which
was deprecated since v26.

The auditor's taler-helper-auditor-coins.c had to be marked as
work-in-progress to adjust for the removal of the old and presence
of the new refresh protocol

Diffstat:
Msrc/auditor/taler-helper-auditor-coins.c | 3+++
Msrc/exchange/Makefile.am | 4----
Msrc/exchange/taler-exchange-httpd.c | 36+-----------------------------------
Dsrc/exchange/taler-exchange-httpd_batch-withdraw.c | 1460-------------------------------------------------------------------------------
Dsrc/exchange/taler-exchange-httpd_batch-withdraw.h | 62--------------------------------------------------------------
Dsrc/exchange/taler-exchange-httpd_csr.c | 352-------------------------------------------------------------------------------
Dsrc/exchange/taler-exchange-httpd_csr.h | 56--------------------------------------------------------
Dsrc/exchange/taler-exchange-httpd_melt.c | 488-------------------------------------------------------------------------------
Dsrc/exchange/taler-exchange-httpd_melt.h | 48------------------------------------------------
Dsrc/exchange/taler-exchange-httpd_refreshes_reveal.c | 1076-------------------------------------------------------------------------------
Msrc/exchangedb/Makefile.am | 2--
Dsrc/exchangedb/perf_get_link_data.c | 546-------------------------------------------------------------------------------
Dsrc/exchangedb/pg_get_melt.c | 124-------------------------------------------------------------------------------
Dsrc/exchangedb/pg_get_melt.h | 44--------------------------------------------
Dsrc/exchangedb/pg_insert_refresh_reveal.c | 109-------------------------------------------------------------------------------
Dsrc/exchangedb/pg_insert_refresh_reveal.h | 51---------------------------------------------------
Msrc/exchangedb/plugin_exchangedb_postgres.c | 10----------
Msrc/include/taler_exchange_service.h | 467+++++++------------------------------------------------------------------------
Msrc/include/taler_exchangedb_plugin.h | 110++-----------------------------------------------------------------------------
Msrc/lib/Makefile.am | 3---
Dsrc/lib/exchange_api_csr_melt.c | 320-------------------------------------------------------------------------------
Dsrc/lib/exchange_api_melt.c | 602-------------------------------------------------------------------------------
Msrc/lib/exchange_api_refresh_common.c | 236+------------------------------------------------------------------------------
Msrc/lib/exchange_api_refresh_common.h | 85-------------------------------------------------------------------------------
Dsrc/lib/exchange_api_refreshes_reveal.c | 533-------------------------------------------------------------------------------
25 files changed, 44 insertions(+), 6783 deletions(-)

diff --git a/src/auditor/taler-helper-auditor-coins.c b/src/auditor/taler-helper-auditor-coins.c @@ -1435,6 +1435,8 @@ refresh_session_cb (void *cls, GNUNET_h2s (&issue->denom_hash.hash), TALER_amount2s (amount_with_fee)); +#pragma message "auditor's reveal requires refactoring for new refresh protocol" +#if FIXME_NEW_REFRESH { struct TALER_Amount refresh_cost; struct RevealContext reveal_ctx = { @@ -1602,6 +1604,7 @@ refresh_session_cb (void *cls, } GNUNET_free (reveal_ctx.new_issues); } +#endif /* update old coin's denomination balance */ dso = get_denomination_summary (cc, diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am @@ -153,14 +153,12 @@ taler_exchange_httpd_SOURCES = \ taler-exchange-httpd_aml-transfer-get.c taler-exchange-httpd_aml-transfer-get.h \ taler-exchange-httpd_auditors.c taler-exchange-httpd_auditors.h \ taler-exchange-httpd_batch-deposit.c taler-exchange-httpd_batch-deposit.h \ - taler-exchange-httpd_batch-withdraw.c taler-exchange-httpd_batch-withdraw.h \ taler-exchange-httpd_blinding-prepare.c taler-exchange-httpd_blinding-prepare.h \ taler-exchange-httpd_coins_get.c taler-exchange-httpd_coins_get.h \ taler-exchange-httpd_common_deposit.c taler-exchange-httpd_common_deposit.h \ taler-exchange-httpd_common_kyc.c taler-exchange-httpd_common_kyc.h \ taler-exchange-httpd_config.c taler-exchange-httpd_config.h \ taler-exchange-httpd_contract.c taler-exchange-httpd_contract.h \ - taler-exchange-httpd_csr.c taler-exchange-httpd_csr.h \ taler-exchange-httpd_db.c taler-exchange-httpd_db.h \ taler-exchange-httpd_deposits_get.c taler-exchange-httpd_deposits_get.h \ taler-exchange-httpd_extensions.c taler-exchange-httpd_extensions.h \ @@ -187,7 +185,6 @@ taler_exchange_httpd_SOURCES = \ taler-exchange-httpd_management_wire_enable.c \ taler-exchange-httpd_management_wire_disable.c \ taler-exchange-httpd_management_wire_fees.c \ - taler-exchange-httpd_melt.c taler-exchange-httpd_melt.h \ taler-exchange-httpd_melt_v27.c taler-exchange-httpd_melt_v27.h \ taler-exchange-httpd_metrics.c taler-exchange-httpd_metrics.h \ taler-exchange-httpd_mhd.c taler-exchange-httpd_mhd.h \ @@ -198,7 +195,6 @@ taler_exchange_httpd_SOURCES = \ taler-exchange-httpd_purses_merge.c taler-exchange-httpd_purses_merge.h \ taler-exchange-httpd_recoup.c taler-exchange-httpd_recoup.h \ taler-exchange-httpd_recoup-refresh.c taler-exchange-httpd_recoup-refresh.h \ - 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_attest.c taler-exchange-httpd_reserves_attest.h \ taler-exchange-httpd_reserves_close.c taler-exchange-httpd_reserves_close.h \ diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c @@ -32,7 +32,6 @@ #include "taler_templating_lib.h" #include "taler_mhd_lib.h" #include "taler-exchange-httpd_withdraw.h" -#include "taler-exchange-httpd_batch-withdraw.h" #include "taler-exchange-httpd_aml-attributes-get.h" #include "taler-exchange-httpd_aml-decision.h" #include "taler-exchange-httpd_aml-statistics-get.h" @@ -44,7 +43,6 @@ #include "taler-exchange-httpd_coins_get.h" #include "taler-exchange-httpd_config.h" #include "taler-exchange-httpd_contract.h" -#include "taler-exchange-httpd_csr.h" #include "taler-exchange-httpd_deposits_get.h" #include "taler-exchange-httpd_extensions.h" #include "taler-exchange-httpd_keys.h" @@ -58,7 +56,6 @@ #include "taler-exchange-httpd_aml-decision.h" #include "taler-exchange-httpd_legitimization-measures-get.h" #include "taler-exchange-httpd_management.h" -#include "taler-exchange-httpd_melt.h" #include "taler-exchange-httpd_melt_v27.h" #include "taler-exchange-httpd_metrics.h" #include "taler-exchange-httpd_mhd.h" @@ -69,7 +66,6 @@ #include "taler-exchange-httpd_purses_merge.h" #include "taler-exchange-httpd_recoup.h" #include "taler-exchange-httpd_recoup-refresh.h" -#include "taler-exchange-httpd_refreshes_reveal.h" #include "taler-exchange-httpd_refund.h" #include "taler-exchange-httpd_reserves_attest.h" #include "taler-exchange-httpd_reserves_close.h" @@ -380,10 +376,6 @@ handle_post_coins (struct TEH_RequestContext *rc, CoinOpHandler handler; } h[] = { - { - .op = "melt", - .handler = &TEH_handler_melt - }, #if FIXME_9828 { .op = "recoup", @@ -762,11 +754,6 @@ handle_post_reserves (struct TEH_RequestContext *rc, } h[] = { { - /* deprecated since v26 */ - .op = "batch-withdraw", - .handler = &TEH_handler_batch_withdraw - }, - { .op = "purse", .handler = &TEH_handler_reserves_purse }, @@ -1666,13 +1653,6 @@ handle_mhd_request (void *cls, .handler.post = &TEH_handler_batch_deposit, .nargs = 0 }, - /* request R, used in clause schnorr withdraw and refresh */ - { - .url = "csr-melt", - .method = MHD_HTTP_METHOD_POST, - .handler.post = &TEH_handler_csr_melt, - .nargs = 0 - }, /* request R's input for Clause-Schnorr signatures in batches */ { .url = "blinding-prepare", @@ -1680,12 +1660,6 @@ handle_mhd_request (void *cls, .handler.post = &TEH_handler_blinding_prepare, .nargs = 0 }, - { - .url = "csr-withdraw", - .method = MHD_HTTP_METHOD_POST, - .handler.post = &TEH_handler_csr_withdraw, - .nargs = 0 - }, /* withdraw request, available since v26 of the API */ { .url = "withdraw", @@ -1713,7 +1687,7 @@ handle_mhd_request (void *cls, .handler.post = &TEH_handler_reveal_withdraw, .nargs = 0 }, - /* reveal-melt operation, introduced with v26 */ + /* reveal-melt operation, introduced with v27 */ { .url = "reveal-melt", .method = MHD_HTTP_METHOD_POST, @@ -1753,13 +1727,6 @@ handle_mhd_request (void *cls, .handler.post = &TEH_handler_melt_v27, .nargs = 0 }, - /* refreshes/$RCH/reveal */ - { - .url = "refreshes", - .method = MHD_HTTP_METHOD_POST, - .handler.post = &TEH_handler_reveal, - .nargs = 2 - }, /* tracking transfers */ { .url = "transfers", @@ -2692,7 +2659,6 @@ do_shutdown (void *cls) my_mhd = TALER_MHD_daemon_stop (); TEH_resume_keys_requests (true); TEH_batch_deposit_cleanup (); - TEH_batch_withdraw_cleanup (); TEH_withdraw_cleanup (); TEH_melt_v27_cleanup (); TEH_reserves_close_cleanup (); diff --git a/src/exchange/taler-exchange-httpd_batch-withdraw.c b/src/exchange/taler-exchange-httpd_batch-withdraw.c @@ -1,1460 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2024 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 <http://www.gnu.org/licenses/> -*/ - -/* - * NOTE: These endpoints are deprecated starting with v26 of the protocol and will be removed, - * including this file. - */ - -/** - * @file taler-exchange-httpd_batch-withdraw.c - * @brief Common code to handle /reserves/$RESERVE_PUB/{age,batch}-withdraw requests - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - * @author Ozgur Kesim - */ -#include "platform.h" -#include <gnunet/gnunet_util_lib.h> -#include <jansson.h> -#include "taler-exchange-httpd.h" -#include "taler_json_lib.h" -#include "taler_kyclogic_lib.h" -#include "taler_mhd_lib.h" -#include "taler-exchange-httpd_batch-withdraw.h" -#include "taler-exchange-httpd_common_kyc.h" -#include "taler-exchange-httpd_responses.h" -#include "taler-exchange-httpd_keys.h" -#include "taler_util.h" - -/** - * Context for batch-withdraw requests - */ -struct BatchWithdrawContext -{ - - /** - * This struct is kept in a DLL. - */ - struct BatchWithdrawContext *prev; - struct BatchWithdrawContext *next; - - /** - * Processing phase we are in for any of the withdraw types. - * The ordering here partially matters, as we progress through - * them by incrementing the phase in the happy path. - */ - enum - { - PHASE_CHECK_KEYS, - PHASE_CHECK_RESERVE_SIGNATURE, - PHASE_RUN_LEGI_CHECK, - PHASE_SUSPENDED, - PHASE_CHECK_KYC_RESULT, - PHASE_PREPARE_TRANSACTION, - PHASE_RUN_TRANSACTION, - PHASE_GENERATE_REPLY_SUCCESS, - PHASE_GENERATE_REPLY_ERROR, - PHASE_RETURN_NO, - PHASE_RETURN_YES, - } phase; - - - /** - * Handle for the legitimization check. - */ - struct TEH_LegitimizationCheckHandle *lch; - - /** - * Request context - */ - const struct TEH_RequestContext *rc; - - /** - * Public key of the reserve. - */ - struct TALER_ReservePublicKeyP reserve_pub; - - /** - * KYC status for the operation. - */ - struct TALER_EXCHANGEDB_KycStatus kyc; - - /** - * Current time for the DB transaction. - */ - struct GNUNET_TIME_Timestamp now; - - /** - * Set to the hash of the normalized payto URI that established - * the reserve. - */ - struct TALER_NormalizedPaytoHashP h_normalized_payto; - - /** - * Number of coins, length of @e planchets array. - */ - unsigned int num_coins; - - /** - * Array of @e num_coins planchets we are processing, - * containing the information per planchet in a batch withdraw. - */ - struct PlanchetContext - { - - /** - * Value of the coin being exchanged (matching the denomination key) - * plus the transaction fee. We include this in what is being - * signed so that we can verify a reserve's remaining total balance - * without needing to access the respective denomination key - * information each time. - */ - struct TALER_Amount amount_with_fee; - - /** - * Blinded planchet. - */ - struct TALER_BlindedPlanchet blinded_planchet; - - /** - * Set to the resulting signed coin data to be returned to the client. - */ - struct TALER_EXCHANGEDB_CollectableBlindcoin collectable; - - } *planchets; - - /** - * Total amount from all coins with fees. - */ - struct TALER_Amount batch_total; - - /** - * Errors occurring during evaluation of the request are captured in this struct. - * In phase PHASE_GENERATE_REPLY_ERROR an appropriate error message is prepared - * and sent to the client. - */ - struct - { - /** - * The different type of errors that might occur, sorted by name. - * Some of them require idempotency checks, which are marked - * in array @a needs_idempotency_check_error below. - */ - enum - { - ERROR_NONE, - - ERROR_AGE_RESTRICTION_REQUIRED, - ERROR_BATCH_AMOUNT_FEE_OVERFLOW, - ERROR_BATCH_IDEMPOTENT_PLANCHET, - ERROR_BATCH_INSUFFICIENT_FUNDS, - ERROR_BATCH_NONCE_RESUSE, - ERROR_CIPHER_MISMATCH, - ERROR_DB_FETCH_FAILED, - ERROR_DB_INVARIANT_FAILURE, - ERROR_DENOMINATION_BATCH_SIGN, - ERROR_DENOMINATION_EXPIRED, - ERROR_DENOMINATION_KEY_UNKNOWN, - ERROR_DENOMINATION_REVOKED, - ERROR_DENOMINATION_VALIDITY_IN_FUTURE, - ERROR_INTERNAL_INVARIANT_FAILURE, - ERROR_KEYS_MISSING, - ERROR_KYC_REQUIRED, - ERROR_LEGITIMIZATION_RESULT, - ERROR_RESERVE_SIGNATURE_INVALID, - ERROR_RESERVE_UNKNOWN, - ERROR_REQUEST_PARAMETER_MALFORMED, - - ERROR_MAX - } code; - - /** - * Some errors require details to be sent to the client. - * These are captured in this union. - * Each field is marked with a comment, referring to the error(s) - * that is/are using it. - */ - union - { - /* ERROR_REQUEST_PARAMETER_MALFORMED */ - const char *hint; - - /* ERROR_DENOMINATION_KEY_UNKNOWN */ - const struct TALER_DenominationHashP *denom_h; - - /* ERROR_DB_FETCH_FAILED */ - const char *db_fetch_context; - - /* ERROR_AGE_RESTRICTION_REQUIRED */ - uint16_t lowest_age; - - /* ERROR_BATCH_INSUFFICIENT_FUNDS */ - struct TALER_Amount reserve_balance; - - /* ERROR_DENOMINATION_BATCH_SIGN */ - enum TALER_ErrorCode ec; - - /* ERROR_LEGITIMIZATION_RESULT */ - struct - { - struct MHD_Response *response; - unsigned int http_status; - } legi; - - } details; - } error; -}; - -/** - * This table marks which @a BatchWithdrawContext.error.code - * needs a idempotency check prior to actually sending an error message. - */ -static const bool - needs_idempotency_check[] = { - [ERROR_NONE] = false, - - [ERROR_AGE_RESTRICTION_REQUIRED] = false, - [ERROR_BATCH_AMOUNT_FEE_OVERFLOW] = false, - [ERROR_BATCH_NONCE_RESUSE] = false, - [ERROR_CIPHER_MISMATCH] = false, - [ERROR_DB_FETCH_FAILED] = false, - [ERROR_DB_INVARIANT_FAILURE] = false, - [ERROR_DENOMINATION_BATCH_SIGN] = false, - [ERROR_DENOMINATION_VALIDITY_IN_FUTURE] = false, - [ERROR_INTERNAL_INVARIANT_FAILURE] = false, - [ERROR_LEGITIMIZATION_RESULT] = false, - [ERROR_REQUEST_PARAMETER_MALFORMED] = false, - [ERROR_RESERVE_SIGNATURE_INVALID] = false, - [ERROR_RESERVE_UNKNOWN] = false, - - /* These require idempotency checks */ - [ERROR_BATCH_IDEMPOTENT_PLANCHET] = true, - [ERROR_BATCH_INSUFFICIENT_FUNDS] = true, - [ERROR_DENOMINATION_EXPIRED] = true, - [ERROR_DENOMINATION_KEY_UNKNOWN] = true, - [ERROR_DENOMINATION_REVOKED] = true, - [ERROR_KEYS_MISSING] = true, - [ERROR_KYC_REQUIRED] = true, -}; - -_Static_assert ( - (sizeof (needs_idempotency_check) == - ERROR_MAX), - "needs_idempotency_check size mismatch with enum WithdrawErrorCode"); - -/** - * The following macros set the given error code, - * set the phase to PHASE_GENERATE_REPLY_ERROR, - * and optionally set the given field (with an optionally given value). - */ -#define SET_ERROR(wc, ec) \ - do \ - { (wc)->error.code = (ec); \ - (wc)->phase = PHASE_GENERATE_REPLY_ERROR; } while (0) - -#define SET_ERROR_WITH_FIELD(wc, ec, field) \ - do \ - { (wc)->error.code = (ec); \ - (wc)->error.details.field = (field); \ - (wc)->phase = PHASE_GENERATE_REPLY_ERROR; } while (0) - -#define SET_ERROR_WITH_DETAIL(wc, ec, field, value) \ - do \ - { (wc)->error.code = (ec); \ - (wc)->error.details.field = (value); \ - (wc)->phase = PHASE_GENERATE_REPLY_ERROR; } while (0) - - -/** - * All withdraw context is kept in a DLL. - */ -static struct BatchWithdrawContext *wc_head; -static struct BatchWithdrawContext *wc_tail; - -void -TEH_batch_withdraw_cleanup () -{ - struct BatchWithdrawContext *wc; - - while (NULL != (wc = wc_head)) - { - GNUNET_CONTAINER_DLL_remove (wc_head, - wc_tail, - wc); - MHD_resume_connection (wc->rc->connection); - } -} - - -/** - * Terminate the main loop by returning the final - * result. - * - * @param[in,out] wc context to update phase for - * @param mres MHD status to return - */ -static void -finish_loop (struct BatchWithdrawContext *wc, - MHD_RESULT mres) -{ - wc->phase = (MHD_YES == mres) - ? PHASE_RETURN_YES - : PHASE_RETURN_NO; -} - - -/** - * Generates our final (successful) response to a batch withdraw request. - * - * @param wc operation context - */ -static void -phase_generate_reply_success (struct BatchWithdrawContext *wc) -{ - const struct TEH_RequestContext *rc = wc->rc; - json_t *sigs; - - sigs = json_array (); - GNUNET_assert (NULL != sigs); - for (unsigned int i = 0; i<wc->num_coins; i++) - { - struct PlanchetContext *pc = &wc->planchets[i]; - - GNUNET_assert ( - 0 == - json_array_append_new ( - sigs, - GNUNET_JSON_PACK ( - TALER_JSON_pack_blinded_denom_sig ( - "ev_sig", - &pc->collectable.sig)))); - } - TEH_METRICS_withdraw_num_coins += wc->num_coins; - finish_loop (wc, - TALER_MHD_REPLY_JSON_PACK ( - rc->connection, - MHD_HTTP_OK, - GNUNET_JSON_pack_array_steal ("ev_sigs", - sigs))); -} - - -/** - * Check if the batch withdraw in @a wc is replayed - * and we already have an answer. - * If so, replay the existing answer and return the HTTP response. - * - * @param wc parsed request data - * @return true if the request is idempotent with an existing request - * false if we did not find the request in the DB and did not set @a mret - */ -static bool -check_idempotency ( - struct BatchWithdrawContext *wc) -{ - - for (unsigned int i = 0; i<wc->num_coins; i++) - { - struct PlanchetContext *pc = &wc->planchets[i]; - enum GNUNET_DB_QueryStatus qs; - struct TALER_EXCHANGEDB_CollectableBlindcoin collectable; - - qs = TEH_plugin->get_batch_withdraw_info ( - TEH_plugin->cls, - &pc->collectable.h_coin_envelope, - &collectable); - if (0 > qs) - { - /* FIXME: soft error not handled correctly! */ - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - SET_ERROR_WITH_DETAIL (wc, - ERROR_DB_FETCH_FAILED, - db_fetch_context, - "get_batch_withdraw_info"); - return true; /* Well, kind-of. */ - } - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - return false; - pc->collectable = collectable; - } - - /* generate idempotent reply */ - TEH_METRICS_num_requests[TEH_MT_REQUEST_IDEMPOTENT_WITHDRAW]++; - wc->phase = PHASE_GENERATE_REPLY_SUCCESS; - return true; -} - - -/** - * Function implementing withdraw transaction. 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. - * - * Note that "wc->collectable.sig" is set before entering this function as we - * signed before entering the transaction. - * - * @param cls a `struct BatchWithdrawContext *`, with @e withdraw_type set to WITHDRAW_TYPE_BATCH - * @param connection MHD request which triggered the transaction - * @param[out] mhd_ret set to MHD response status for @a connection, - * if transaction failed (!) - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -batch_withdraw_transaction ( - void *cls, - struct MHD_Connection *connection, - MHD_RESULT *mhd_ret) -{ - struct BatchWithdrawContext *wc = cls; - uint64_t ruuid; - enum GNUNET_DB_QueryStatus qs; - bool found = false; - bool balance_ok = false; - bool age_ok = false; - uint16_t allowed_maximum_age = 0; - struct TALER_Amount reserve_balance; - - qs = TEH_plugin->do_batch_withdraw ( - TEH_plugin->cls, - wc->now, - &wc->reserve_pub, - &wc->batch_total, - TEH_age_restriction_enabled, - &found, - &balance_ok, - &reserve_balance, - &age_ok, - &allowed_maximum_age, - &ruuid); - - if (0 > qs) - { - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - { - GNUNET_break (0); - SET_ERROR_WITH_DETAIL (wc, - ERROR_DB_FETCH_FAILED, - db_fetch_context, - "update_reserve_batch_withdraw"); - } - return qs; - } - - if (! found) - { - GNUNET_break_op (0); - SET_ERROR (wc, - ERROR_RESERVE_UNKNOWN); - return GNUNET_DB_STATUS_HARD_ERROR; - } - - if (! age_ok) - { - /* We respond with the lowest age in the corresponding age group - * of the required age */ - uint16_t lowest_age = TALER_get_lowest_age ( - &TEH_age_restriction_config.mask, - allowed_maximum_age); - SET_ERROR_WITH_FIELD (wc, - ERROR_AGE_RESTRICTION_REQUIRED, - lowest_age); - return GNUNET_DB_STATUS_HARD_ERROR; - } - - if (! balance_ok) - { - GNUNET_break_op (0); - SET_ERROR_WITH_FIELD (wc, - ERROR_BATCH_INSUFFICIENT_FUNDS, - reserve_balance); - return GNUNET_DB_STATUS_HARD_ERROR; - } - - /* Add information about each planchet in the batch */ - for (unsigned int i = 0; i<wc->num_coins; i++) - { - struct PlanchetContext *pc = &wc->planchets[i]; - const struct TALER_BlindedPlanchet *bp = &pc->blinded_planchet; - const union GNUNET_CRYPTO_BlindSessionNonce *nonce = NULL; - bool denom_unknown = true; - bool conflict = true; - bool nonce_reuse = true; - - switch (bp->blinded_message->cipher) - { - case GNUNET_CRYPTO_BSA_INVALID: - break; - case GNUNET_CRYPTO_BSA_RSA: - break; - case GNUNET_CRYPTO_BSA_CS: - nonce = (const union GNUNET_CRYPTO_BlindSessionNonce *) - &bp->blinded_message->details.cs_blinded_message.nonce; - break; - } - - qs = TEH_plugin->do_batch_withdraw_insert ( - TEH_plugin->cls, - nonce, - &pc->collectable, - wc->now, - ruuid, - &denom_unknown, - &conflict, - &nonce_reuse); - - if (0 > qs) - { - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - SET_ERROR_WITH_DETAIL (wc, - ERROR_DB_FETCH_FAILED, - db_fetch_context, - "do_batch_withdraw_insert"); - return qs; - } - - if (denom_unknown) - { - GNUNET_break (0); - SET_ERROR (wc, - ERROR_DB_INVARIANT_FAILURE); - return GNUNET_DB_STATUS_HARD_ERROR; - } - - if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) || - (conflict) ) - { - SET_ERROR (wc, - ERROR_BATCH_IDEMPOTENT_PLANCHET); - return GNUNET_DB_STATUS_HARD_ERROR; - } - - if (nonce_reuse) - { - GNUNET_break_op (0); - SET_ERROR (wc, - ERROR_BATCH_NONCE_RESUSE); - - return GNUNET_DB_STATUS_HARD_ERROR; - } - } - TEH_METRICS_num_success[TEH_MT_SUCCESS_WITHDRAW]++; - return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; -} - - -/** - * The request was prepared successfully. - * Run the main DB transaction. - * - * @param wc The context for the current withdraw request - */ -static void -phase_run_transaction ( - struct BatchWithdrawContext *wc) -{ - MHD_RESULT mhd_ret; - enum GNUNET_GenericReturnValue qs; - - GNUNET_assert (PHASE_RUN_TRANSACTION == - wc->phase); - - qs = TEH_DB_run_transaction (wc->rc->connection, - "run batch withdraw", - TEH_MT_REQUEST_WITHDRAW, - &mhd_ret, - &batch_withdraw_transaction, - wc); - if (GNUNET_OK != qs) - { - /* TODO[oec]: Logic still ok with new error handling? */ - if (PHASE_RUN_TRANSACTION == wc->phase) - finish_loop (wc, - mhd_ret); - return; - } - wc->phase++; -} - - -/** - * The request for batch withdraw was parsed successfully. - * Prepare our side for the main DB transaction. - * - * @param wc context for request processing, with @e withdraw_type set to WITHDRAW_TYPE_BATCH - * @return GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -batch_withdraw_phase_prepare_transaction (struct BatchWithdrawContext *wc) -{ - struct TALER_BlindedDenominationSignature bss[wc->num_coins]; - struct TEH_CoinSignData csds[wc->num_coins]; - - for (unsigned int i = 0; i<wc->num_coins; i++) - { - struct PlanchetContext *pc = &wc->planchets[i]; - struct TEH_CoinSignData *csdsi = &csds[i]; - - csdsi->h_denom_pub = &pc->collectable.denom_pub_hash; - csdsi->bp = &pc->blinded_planchet; - } - { - enum TALER_ErrorCode ec; - - ec = TEH_keys_denomination_batch_sign ( - wc->num_coins, - csds, - false, - bss); - if (TALER_EC_NONE != ec) - { - GNUNET_break (0); - SET_ERROR_WITH_FIELD (wc, - ERROR_DENOMINATION_BATCH_SIGN, - ec); - return GNUNET_SYSERR; - } - } - - for (unsigned int i = 0; i<wc->num_coins; i++) - { - struct PlanchetContext *pc = &wc->planchets[i]; - - pc->collectable.sig = bss[i]; - } - - return GNUNET_OK; -} - - -/** - * The request for withdraw was parsed successfully. - * Choose the appropriate preparation step depending on @e withdraw_type - */ -static void -phase_prepare_transaction ( - struct BatchWithdrawContext *wc) -{ - enum GNUNET_GenericReturnValue r; - r = batch_withdraw_phase_prepare_transaction (wc); - if (GNUNET_OK != r) - return; - wc->phase++; -} - - -/** - * Check the KYC result. - * - * @param wc context for request processing - */ -static void -phase_check_kyc_result (struct BatchWithdrawContext *wc) -{ - /* return final positive response */ - if (! wc->kyc.ok) - { - SET_ERROR (wc, - ERROR_KYC_REQUIRED); - return; - } - wc->phase++; -} - - -/** - * Function called with the result of a legitimization - * check. - * - * @param cls closure - * @param lcr legitimization check result - */ -static void -withdraw_legi_cb ( - void *cls, - const struct TEH_LegitimizationCheckResult *lcr) -{ - struct BatchWithdrawContext *wc = cls; - - wc->lch = NULL; - GNUNET_assert (PHASE_SUSPENDED == - wc->phase); - MHD_resume_connection (wc->rc->connection); - GNUNET_CONTAINER_DLL_remove (wc_head, - wc_tail, - wc); - TALER_MHD_daemon_trigger (); - if (NULL != lcr->response) - { - wc->error.details.legi.response = lcr->response; - wc->error.details.legi.http_status = lcr->http_status; - SET_ERROR (wc, - ERROR_LEGITIMIZATION_RESULT); - return; - } - wc->kyc = lcr->kyc; - wc->phase = PHASE_CHECK_KYC_RESULT; -} - - -/** - * Function called to iterate over KYC-relevant transaction amounts for a - * particular time range. Called within a database transaction, so must - * not start a new one. - * - * @param cls closure, identifies the event type and account to iterate - * over events for - * @param limit maximum time-range for which events should be fetched - * (timestamp in the past) - * @param cb function to call on each event found, events must be returned - * in reverse chronological order - * @param cb_cls closure for @a cb, of type struct BatchWithdrawContext - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -withdraw_amount_cb ( - void *cls, - struct GNUNET_TIME_Absolute limit, - TALER_EXCHANGEDB_KycAmountCallback cb, - void *cb_cls) -{ - struct BatchWithdrawContext *wc = cls; - enum GNUNET_GenericReturnValue ret; - enum GNUNET_DB_QueryStatus qs; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Signaling amount %s for KYC check during batch-withdrawal\n", - TALER_amount2s (&wc->batch_total)); - - ret = cb (cb_cls, - &wc->batch_total, - wc->now.abs_time); - - GNUNET_break (GNUNET_SYSERR != ret); - - if (GNUNET_OK != ret) - return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; - - qs = TEH_plugin->select_withdraw_amounts_for_kyc_check ( - TEH_plugin->cls, - &wc->h_normalized_payto, - limit, - cb, - cb_cls); - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Got %d additional transactions for this batch-withdrawal and limit %llu\n", - qs, - (unsigned long long) limit.abs_value_us); - - GNUNET_break (qs >= 0); - - return qs; -} - - -/** - * Do legitimization check. - * - * @param wc operation context - */ -static void -phase_run_legi_check (struct BatchWithdrawContext *wc) -{ - enum GNUNET_DB_QueryStatus qs; - struct TALER_FullPayto payto_uri; - struct TALER_FullPaytoHashP h_full_payto; - - /* Check if the money came from a wire transfer */ - qs = TEH_plugin->reserves_get_origin ( - TEH_plugin->cls, - &wc->reserve_pub, - &h_full_payto, - &payto_uri); - if (qs < 0) - { - SET_ERROR_WITH_DETAIL (wc, - ERROR_DB_FETCH_FAILED, - db_fetch_context, - "reserves_get_origin"); - return; - } - /* If _no_ results, reserve was created by merge, - in which case no KYC check is required as the - merge already did that. */ - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - { - wc->phase = PHASE_PREPARE_TRANSACTION; - return; - } - TALER_full_payto_normalize_and_hash (payto_uri, - &wc->h_normalized_payto); - wc->lch = TEH_legitimization_check ( - &wc->rc->async_scope_id, - TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW, - payto_uri, - &wc->h_normalized_payto, - NULL, /* no account pub: this is about the origin account */ - &withdraw_amount_cb, - wc, - &withdraw_legi_cb, - wc); - GNUNET_assert (NULL != wc->lch); - GNUNET_free (payto_uri.full_payto); - GNUNET_CONTAINER_DLL_insert (wc_head, - wc_tail, - wc); - MHD_suspend_connection (wc->rc->connection); - wc->phase = PHASE_SUSPENDED; -} - - -/** - * Check if the given denomination is still or already valid, has not been - * revoked and potentically supports age restriction. - * - * @param[in,out] wc context for the withdraw operation - * @param ksh The handle to the current state of (denomination) keys in the exchange - * @param denom_h Hash of the denomination key to check - * @param[out] pdk denomination key found, might be NULL - * @return GNUNET_OK when denomation was found and valid, - * GNUNET_NO when denomination was not valid but request was idempotent, - * GNUNET_SYSERR otherwise (denomination invalid), with finish_loop called. - */ -static enum GNUNET_GenericReturnValue -find_denomination ( - struct BatchWithdrawContext *wc, - struct TEH_KeyStateHandle *ksh, - const struct TALER_DenominationHashP *denom_h, - struct TEH_DenominationKey **pdk) -{ - struct TEH_DenominationKey *dk; - - *pdk = NULL; - - dk = TEH_keys_denomination_by_hash_from_state ( - ksh, - denom_h, - NULL, - NULL); - - if (NULL == dk) - { - SET_ERROR_WITH_FIELD (wc, - ERROR_DENOMINATION_KEY_UNKNOWN, - denom_h); - return GNUNET_NO; - } - - if (GNUNET_TIME_absolute_is_past ( - dk->meta.expire_withdraw.abs_time)) - { - SET_ERROR_WITH_FIELD (wc, - ERROR_DENOMINATION_EXPIRED, - denom_h); - return GNUNET_SYSERR; - } - - if (GNUNET_TIME_absolute_is_future ( - dk->meta.start.abs_time)) - { - GNUNET_break_op (0); - SET_ERROR_WITH_FIELD (wc, - ERROR_DENOMINATION_VALIDITY_IN_FUTURE, - denom_h); - return GNUNET_SYSERR; - } - - if (dk->recoup_possible) - { - SET_ERROR (wc, - ERROR_DENOMINATION_REVOKED); - return GNUNET_SYSERR; - } - - *pdk = dk; - return GNUNET_OK; -} - - -/** - * Check if the keys in the request are valid for batch withdrawal. - * - * @param[in,out] wc context for the batch withdraw request processing - * @param ksh key state handle - * @return GNUNET_OK on success, - * GNUNET_NO on error (and response being sent) - */ -static enum GNUNET_GenericReturnValue -batch_withdraw_phase_check_keys ( - struct BatchWithdrawContext *wc, - struct TEH_KeyStateHandle *ksh) -{ - for (unsigned int i = 0; i<wc->num_coins; i++) - { - struct PlanchetContext *pc = &wc->planchets[i]; - struct TEH_DenominationKey *dk; - enum GNUNET_GenericReturnValue r; - - r = find_denomination (wc, - ksh, - &pc->collectable.denom_pub_hash, - &dk); - - if (GNUNET_OK != r) - return GNUNET_NO; - - GNUNET_assert (NULL != dk); - - if (dk->denom_pub.bsign_pub_key->cipher != - pc->blinded_planchet.blinded_message->cipher) - { - /* denomination cipher and blinded planchet cipher not the same */ - GNUNET_break_op (0); - SET_ERROR (wc, - ERROR_CIPHER_MISMATCH); - return GNUNET_NO; - } - - if (0 > - TALER_amount_add (&pc->collectable.amount_with_fee, - &dk->meta.value, - &dk->meta.fees.withdraw)) - { - GNUNET_break (0); - SET_ERROR (wc, - ERROR_BATCH_AMOUNT_FEE_OVERFLOW); - return GNUNET_NO; - } - - if (0 > - TALER_amount_add (&wc->batch_total, - &wc->batch_total, - &pc->collectable.amount_with_fee)) - { - GNUNET_break (0); - SET_ERROR (wc, - ERROR_BATCH_AMOUNT_FEE_OVERFLOW); - return GNUNET_NO; - } - - TALER_coin_ev_hash (&pc->blinded_planchet, - &pc->collectable.denom_pub_hash, - &pc->collectable.h_coin_envelope); - - TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; - if (GNUNET_OK != - TALER_wallet_withdraw_verify_pre26 ( - &pc->collectable.denom_pub_hash, - &pc->collectable.amount_with_fee, - &pc->collectable.h_coin_envelope, - &pc->collectable.reserve_pub, - &pc->collectable.reserve_sig)) - { - GNUNET_break_op (0); - SET_ERROR (wc, - ERROR_RESERVE_SIGNATURE_INVALID); - return GNUNET_NO; - } - } - /* everything parsed */ - - return GNUNET_OK; -} - - -/** - * Check if the keys in the request are valid for withdrawing. - * - * @param[in,out] wc context for request processing - */ -static void -phase_check_keys (struct BatchWithdrawContext *wc) -{ - struct TEH_KeyStateHandle *ksh; - enum GNUNET_GenericReturnValue r; - - ksh = TEH_keys_get_state (); - if (NULL == ksh) - { - GNUNET_break (0); - SET_ERROR (wc, - ERROR_KEYS_MISSING); - return; - } - - r = batch_withdraw_phase_check_keys (wc, ksh); - - switch (r) - { - case GNUNET_OK: - wc->phase++; - break; - case GNUNET_NO: - /* error generated by function, simply return*/ - break; - case GNUNET_SYSERR: - GNUNET_break (0); - SET_ERROR (wc, - ERROR_KEYS_MISSING); - break; - default: - GNUNET_break (0); - } -} - - -/** - * Cleanup routine for withdraw reqwuest. - * The function is called upon completion of the request - * that should clean up @a rh_ctx. Can be NULL. - * - * @param rc request context to clean up - */ -static void -clean_withdraw_rc (struct TEH_RequestContext *rc) -{ - struct BatchWithdrawContext *wc = rc->rh_ctx; - - if (NULL != wc->lch) - { - TEH_legitimization_check_cancel (wc->lch); - wc->lch = NULL; - } - - for (unsigned int i = 0; i<wc->num_coins; i++) - { - struct PlanchetContext *pc = &wc->planchets[i]; - - TALER_blinded_planchet_free (&pc->blinded_planchet); - TALER_blinded_denom_sig_free (&pc->collectable.sig); - } - GNUNET_free (wc->planchets); - - if (ERROR_LEGITIMIZATION_RESULT == wc->error.code && - NULL != wc->error.details.legi.response) - { - MHD_destroy_response (wc->error.details.legi.response); - wc->error.details.legi.response = NULL; - } - - GNUNET_free (wc); -} - - -/** - * Reports an error, potentially with details. - * That is, it puts a error-type specific response into the MHD queue. - * It will do a idempotency check first, if needed for the error type. - * - * @param wc withdraw context - */ -static void -phase_generate_reply_error ( - struct BatchWithdrawContext *wc) -{ - GNUNET_assert (PHASE_GENERATE_REPLY_ERROR == wc->phase); - GNUNET_assert (ERROR_NONE != wc->error.code); - GNUNET_assert (ERROR_MAX != wc->error.code); - - if (needs_idempotency_check[wc->error.code] && - check_idempotency (wc)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "request is idempotent\n"); - return; - } - - switch (wc->error.code) - { - case ERROR_MAX: - case ERROR_NONE: - { - GNUNET_break (0); - wc->phase = PHASE_RETURN_YES; - return; - } - - case ERROR_REQUEST_PARAMETER_MALFORMED: - TALER_MHD_reply_with_error ( - wc->rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - wc->error.details.hint); - break; - - case ERROR_KEYS_MISSING: - finish_loop (wc, - TALER_MHD_reply_with_error ( - wc->rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, - NULL)); - break; - - case ERROR_DB_FETCH_FAILED: - finish_loop (wc, - TALER_MHD_reply_with_error ( - wc->rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - wc->error.details.db_fetch_context)); - break; - - case ERROR_DB_INVARIANT_FAILURE: - finish_loop (wc, - TALER_MHD_reply_with_error ( - wc->rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_INVARIANT_FAILURE, - NULL)); - break; - - case ERROR_INTERNAL_INVARIANT_FAILURE: - finish_loop (wc, - TALER_MHD_reply_with_error ( - wc->rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - NULL)); - break; - - case ERROR_RESERVE_UNKNOWN: - finish_loop (wc, - TALER_MHD_reply_with_ec ( - wc->rc->connection, - TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN, - NULL)); - break; - - case ERROR_DENOMINATION_BATCH_SIGN: - finish_loop (wc, - TALER_MHD_reply_with_ec ( - wc->rc->connection, - wc->error.details.ec, - NULL)); - break; - - case ERROR_KYC_REQUIRED: - finish_loop (wc, - TEH_RESPONSE_reply_kyc_required ( - wc->rc->connection, - &wc->h_normalized_payto, - &wc->kyc, - false)); - break; - - case ERROR_DENOMINATION_KEY_UNKNOWN: - { - GNUNET_break_op (0); - finish_loop (wc, - TEH_RESPONSE_reply_unknown_denom_pub_hash ( - wc->rc->connection, - wc->error.details.denom_h)); - break; - } - - case ERROR_DENOMINATION_EXPIRED: - GNUNET_break_op (0); - finish_loop (wc, - TEH_RESPONSE_reply_expired_denom_pub_hash ( - wc->rc->connection, - wc->error.details.denom_h, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, - "batch-withdraw")); - break; - - case ERROR_DENOMINATION_VALIDITY_IN_FUTURE: - finish_loop (wc, - TEH_RESPONSE_reply_expired_denom_pub_hash ( - wc->rc->connection, - wc->error.details.denom_h, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, - "batch-withdraw")); - break; - - case ERROR_DENOMINATION_REVOKED: - GNUNET_break_op (0); - finish_loop (wc, - TALER_MHD_reply_with_ec ( - wc->rc->connection, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED, - "batch-withdraw")); - break; - - case ERROR_CIPHER_MISMATCH: - finish_loop (wc, - TALER_MHD_reply_with_ec ( - wc->rc->connection, - TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH, - NULL)); - break; - - case ERROR_AGE_RESTRICTION_REQUIRED: - finish_loop (wc, - TEH_RESPONSE_reply_reserve_age_restriction_required ( - wc->rc->connection, - wc->error.details.lowest_age)); - break; - - case ERROR_BATCH_INSUFFICIENT_FUNDS: - finish_loop (wc, - TEH_RESPONSE_reply_reserve_insufficient_balance ( - wc->rc->connection, - TALER_EC_EXCHANGE_WITHDRAW_INSUFFICIENT_FUNDS, - &wc->error.details.reserve_balance, - &wc->batch_total, - &wc->reserve_pub)); - break; - - case ERROR_BATCH_IDEMPOTENT_PLANCHET: - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Idempotent coin in batch, not allowed. Aborting.\n"); - finish_loop (wc, - TALER_MHD_reply_with_error ( - wc->rc->connection, - MHD_HTTP_CONFLICT, - TALER_EC_EXCHANGE_WITHDRAW_IDEMPOTENT_PLANCHET, - NULL)); - break; - } - - case ERROR_BATCH_NONCE_RESUSE: - finish_loop (wc, - TALER_MHD_reply_with_error ( - wc->rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_WITHDRAW_NONCE_REUSE, - NULL)); - break; - - case ERROR_BATCH_AMOUNT_FEE_OVERFLOW: - finish_loop (wc, - TALER_MHD_reply_with_error ( - wc->rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_WITHDRAW_AMOUNT_FEE_OVERFLOW, - NULL)); - break; - - case ERROR_RESERVE_SIGNATURE_INVALID: - finish_loop (wc, - TALER_MHD_reply_with_ec ( - wc->rc->connection, - TALER_EC_EXCHANGE_WITHDRAW_RESERVE_SIGNATURE_INVALID, - NULL)); - break; - - case ERROR_LEGITIMIZATION_RESULT: { - finish_loop (wc, - MHD_queue_response (wc->rc->connection, - wc->error.details.legi.http_status, - wc->error.details.legi.response)); - break; - } - } -} - - -/** - * Creates a new context for the incoming batch-withdraw request - * - * @param[in,out] wc context of the batch-witrhdraw, to be filled - * @param root json body of the request - * @return GNUNET_OK on success, GNUNET_SYSERR otherwise (response sent) - */ -static enum GNUNET_GenericReturnValue -batch_withdraw_new_request ( - struct BatchWithdrawContext *wc, - const json_t *root) -{ - const json_t *planchets; - - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (TEH_currency, - &wc->batch_total)); - - { - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("planchets", - &planchets), - GNUNET_JSON_spec_end () - }; - - { - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_data (wc->rc->connection, - root, - spec); - if (GNUNET_OK != res) - return res; - } - } - - wc->num_coins = json_array_size (planchets); - if (0 == wc->num_coins) - { - GNUNET_break_op (0); - SET_ERROR_WITH_DETAIL (wc, - ERROR_REQUEST_PARAMETER_MALFORMED, - hint, - "planchets"); - return GNUNET_SYSERR; - } - - if (wc->num_coins > TALER_MAX_COINS) - { - GNUNET_break_op (0); - SET_ERROR_WITH_DETAIL (wc, - ERROR_REQUEST_PARAMETER_MALFORMED, - hint, - "too many planchets"); - return GNUNET_SYSERR; - } - - wc->planchets - = GNUNET_new_array (wc->num_coins, - struct PlanchetContext); - - for (unsigned int i = 0; i<wc->num_coins; i++) - { - struct PlanchetContext *pc = &wc->planchets[i]; - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_fixed_auto ( - "reserve_sig", - &pc->collectable.reserve_sig), - GNUNET_JSON_spec_fixed_auto ( - "denom_pub_hash", - &pc->collectable.denom_pub_hash), - TALER_JSON_spec_blinded_planchet ( - "coin_ev", - &pc->blinded_planchet), - GNUNET_JSON_spec_end () - }; - - { - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_data ( - wc->rc->connection, - json_array_get (planchets, i), - ispec); - if (GNUNET_OK != res) - return res; - } - - pc->collectable.reserve_pub = wc->reserve_pub; - for (unsigned int k = 0; k<i; k++) - { - const struct PlanchetContext *kpc = &wc->planchets[k]; - - if (0 == - TALER_blinded_planchet_cmp ( - &kpc->blinded_planchet, - &pc->blinded_planchet)) - { - GNUNET_break_op (0); - SET_ERROR_WITH_DETAIL (wc, - ERROR_REQUEST_PARAMETER_MALFORMED, - hint, - "duplicate planchet"); - return GNUNET_SYSERR; - } - } - } - return GNUNET_OK; -} - - -MHD_RESULT -TEH_handler_batch_withdraw ( - struct TEH_RequestContext *rc, - const struct TALER_ReservePublicKeyP *reserve_pub, - const json_t *root) -{ - struct BatchWithdrawContext *wc = rc->rh_ctx; - enum GNUNET_GenericReturnValue r; - - if (NULL == wc) - { - wc = GNUNET_new (struct BatchWithdrawContext); - rc->rh_ctx = wc; - rc->rh_cleaner = &clean_withdraw_rc; - wc->rc = rc; - wc->now = GNUNET_TIME_timestamp_get (); - wc->reserve_pub = *reserve_pub; - - r = batch_withdraw_new_request (wc, root); - - if (GNUNET_OK != r) - return (GNUNET_SYSERR == r) ? MHD_NO : MHD_YES; - - wc->phase = PHASE_CHECK_KEYS; - } - - while (true) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "batch-withdraw processing in phase %d\n", - wc->phase); - - switch (wc->phase) - { - case PHASE_CHECK_KEYS: - phase_check_keys (wc); - break; - case PHASE_CHECK_RESERVE_SIGNATURE: - /* signature checks has occurred in batch_withdraw_phase_check_keys */ - wc->phase++; - break; - case PHASE_RUN_LEGI_CHECK: - phase_run_legi_check (wc); - break; - case PHASE_SUSPENDED: - return MHD_YES; - case PHASE_CHECK_KYC_RESULT: - phase_check_kyc_result (wc); - break; - case PHASE_PREPARE_TRANSACTION: - phase_prepare_transaction (wc); - break; - case PHASE_RUN_TRANSACTION: - phase_run_transaction (wc); - break; - case PHASE_GENERATE_REPLY_SUCCESS: - phase_generate_reply_success (wc); - break; - case PHASE_GENERATE_REPLY_ERROR: - phase_generate_reply_error (wc); - break; - case PHASE_RETURN_YES: - return MHD_YES; - case PHASE_RETURN_NO: - return MHD_NO; - } - } -} - - -/* end of taler-exchange-httpd_batch-withdraw.c */ diff --git a/src/exchange/taler-exchange-httpd_batch-withdraw.h b/src/exchange/taler-exchange-httpd_batch-withdraw.h @@ -1,62 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2024 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 <http://www.gnu.org/licenses/> -*/ - -/* - * NOTE: This endpoint is deprecated starting with v24 of the protocol and - * will be removed, including this file. - */ - -/** - * @file taler-exchange-httpd_batch-withdraw.h - * @brief Handle /reserve/$RESERVE_PUB/batch-withdraw requests - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - * @author Özgür Kesim - */ -#ifndef TALER_EXCHANGE_HTTPD_BATCH_WITHDRAW_H -#define TALER_EXCHANGE_HTTPD_BATCH_WITHDRAW_H - -#include <microhttpd.h> -#include "taler-exchange-httpd.h" - -/** - * Resume suspended connections, we are shutting down. - */ -void -TEH_batch_withdraw_cleanup (void); - - -/** - * Handle a "/reserves/$RESERVE_PUB/batch-withdraw" request. Parses the batch of - * 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 passed down to execute the withdrawal - * operation. - * - * @param rc request context - * @param root uploaded JSON data - * @param reserve_pub public key of the reserve - * @return MHD result code - */ -MHD_RESULT -TEH_handler_batch_withdraw ( - struct TEH_RequestContext *rc, - const struct TALER_ReservePublicKeyP *reserve_pub, - const json_t *root); - -#endif diff --git a/src/exchange/taler-exchange-httpd_csr.c b/src/exchange/taler-exchange-httpd_csr.c @@ -1,352 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2023 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 <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-exchange-httpd_csr.c - * @brief Handle /csr requests - * @author Lucien Heuzeveldt - * @author Gian Demarmles - * @author Christian Grothoff - */ -#include "platform.h" -#include <gnunet/gnunet_util_lib.h> -#include <jansson.h> -#include "taler_json_lib.h" -#include "taler_mhd_lib.h" -#include "taler-exchange-httpd_csr.h" -#include "taler-exchange-httpd_responses.h" -#include "taler-exchange-httpd_keys.h" - - -MHD_RESULT -TEH_handler_csr_melt (struct TEH_RequestContext *rc, - const json_t *root, - const char *const args[]) -{ - struct TALER_RefreshMasterSecretP rms; - unsigned int csr_requests_num; - const json_t *csr_requests; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("rms", - &rms), - GNUNET_JSON_spec_array_const ("nks", - &csr_requests), - GNUNET_JSON_spec_end () - }; - enum TALER_ErrorCode ec; - struct TEH_DenominationKey *dk; - - (void) args; - /* parse input */ - { - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_data (rc->connection, - root, - spec); - if (GNUNET_OK != res) - return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; - } - csr_requests_num = json_array_size (csr_requests); - if ( (TALER_MAX_COINS <= csr_requests_num) || - (0 == csr_requests_num) ) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error ( - rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_GENERIC_NEW_DENOMS_ARRAY_SIZE_EXCESSIVE, - NULL); - } - - { - struct GNUNET_CRYPTO_BlindingInputValues ewvs[csr_requests_num]; - { - struct GNUNET_CRYPTO_CsSessionNonce nonces[csr_requests_num]; - struct TALER_DenominationHashP denom_pub_hashes[csr_requests_num]; - struct TEH_CsDeriveData cdds[csr_requests_num]; - struct GNUNET_CRYPTO_CSPublicRPairP r_pubs[csr_requests_num]; - - for (unsigned int i = 0; i < csr_requests_num; i++) - { - uint32_t coin_off; - struct TALER_DenominationHashP *denom_pub_hash = &denom_pub_hashes[i]; - struct GNUNET_JSON_Specification csr_spec[] = { - GNUNET_JSON_spec_uint32 ("coin_offset", - &coin_off), - GNUNET_JSON_spec_fixed_auto ("denom_pub_hash", - denom_pub_hash), - GNUNET_JSON_spec_end () - }; - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_array (rc->connection, - csr_requests, - csr_spec, - i, - -1); - if (GNUNET_OK != res) - { - return (GNUNET_NO == res) ? MHD_YES : MHD_NO; - } - TALER_cs_refresh_nonce_derive (&rms, - coin_off, - &nonces[i]); - } - - for (unsigned int i = 0; i < csr_requests_num; i++) - { - const struct GNUNET_CRYPTO_CsSessionNonce *nonce = &nonces[i]; - const struct TALER_DenominationHashP *denom_pub_hash = - &denom_pub_hashes[i]; - - ewvs[i].cipher = GNUNET_CRYPTO_BSA_CS; - /* check denomination referenced by denom_pub_hash */ - { - struct TEH_KeyStateHandle *ksh; - - ksh = TEH_keys_get_state (); - if (NULL == ksh) - { - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, - NULL); - } - dk = TEH_keys_denomination_by_hash_from_state (ksh, - denom_pub_hash, - NULL, - NULL); - if (NULL == dk) - { - return TEH_RESPONSE_reply_unknown_denom_pub_hash ( - rc->connection, - &denom_pub_hash[i]); - } - if (GNUNET_TIME_absolute_is_past (dk->meta.expire_withdraw.abs_time)) - { - /* This denomination is past the expiration time for withdraws/refreshes*/ - return TEH_RESPONSE_reply_expired_denom_pub_hash ( - rc->connection, - denom_pub_hash, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, - "csr-melt"); - } - if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time)) - { - /* This denomination is not yet valid, no need to check - for idempotency! */ - return TEH_RESPONSE_reply_expired_denom_pub_hash ( - rc->connection, - denom_pub_hash, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, - "csr-melt"); - } - if (dk->recoup_possible) - { - /* This denomination has been revoked */ - return TEH_RESPONSE_reply_expired_denom_pub_hash ( - rc->connection, - denom_pub_hash, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED, - "csr-melt"); - } - if (GNUNET_CRYPTO_BSA_CS != - dk->denom_pub.bsign_pub_key->cipher) - { - /* denomination is valid but not for CS */ - return TEH_RESPONSE_reply_invalid_denom_cipher_for_operation ( - rc->connection, - denom_pub_hash); - } - } - cdds[i].h_denom_pub = denom_pub_hash; - cdds[i].nonce = nonce; - } /* for (i) */ - ec = TEH_keys_denomination_cs_batch_r_pub_simple ( - csr_requests_num, - cdds, - true, - r_pubs); - if (TALER_EC_NONE != ec) - { - GNUNET_break (0); - return TALER_MHD_reply_with_ec (rc->connection, - ec, - NULL); - } - for (unsigned int i = 0; i < csr_requests_num; i++) - ewvs[i].details.cs_values = r_pubs[i]; - } /* end scope */ - - /* send response */ - { - json_t *csr_response_ewvs; - json_t *csr_response; - - csr_response_ewvs = json_array (); - for (unsigned int i = 0; i < csr_requests_num; i++) - { - json_t *csr_obj; - struct TALER_ExchangeBlindingValues exw = { - .blinding_inputs = &ewvs[i] - }; - - csr_obj = GNUNET_JSON_PACK ( - TALER_JSON_pack_exchange_blinding_values ("ewv", - &exw)); - GNUNET_assert (NULL != csr_obj); - GNUNET_assert (0 == - json_array_append_new (csr_response_ewvs, - csr_obj)); - } - csr_response = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_array_steal ("ewvs", - csr_response_ewvs)); - GNUNET_assert (NULL != csr_response); - return TALER_MHD_reply_json_steal (rc->connection, - csr_response, - MHD_HTTP_OK); - } - } -} - - -MHD_RESULT -TEH_handler_csr_withdraw (struct TEH_RequestContext *rc, - const json_t *root, - const char *const args[]) -{ - struct GNUNET_CRYPTO_CsSessionNonce nonce; - struct TALER_DenominationHashP denom_pub_hash; - struct GNUNET_CRYPTO_BlindingInputValues ewv = { - .cipher = GNUNET_CRYPTO_BSA_CS - }; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("nonce", - &nonce), - GNUNET_JSON_spec_fixed_auto ("denom_pub_hash", - &denom_pub_hash), - GNUNET_JSON_spec_end () - }; - struct TEH_DenominationKey *dk; - - (void) args; - { - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_data (rc->connection, - root, - spec); - if (GNUNET_OK != res) - return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; - } - - { - struct TEH_KeyStateHandle *ksh; - - ksh = TEH_keys_get_state (); - if (NULL == ksh) - { - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, - NULL); - } - dk = TEH_keys_denomination_by_hash_from_state (ksh, - &denom_pub_hash, - NULL, - NULL); - if (NULL == dk) - { - return TEH_RESPONSE_reply_unknown_denom_pub_hash ( - rc->connection, - &denom_pub_hash); - } - if (GNUNET_TIME_absolute_is_past (dk->meta.expire_withdraw.abs_time)) - { - /* This denomination is past the expiration time for withdraws/refreshes*/ - return TEH_RESPONSE_reply_expired_denom_pub_hash ( - rc->connection, - &denom_pub_hash, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, - "csr-withdraw"); - } - if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time)) - { - /* This denomination is not yet valid, no need to check - for idempotency! */ - return TEH_RESPONSE_reply_expired_denom_pub_hash ( - rc->connection, - &denom_pub_hash, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, - "csr-withdraw"); - } - if (dk->recoup_possible) - { - /* This denomination has been revoked */ - return TEH_RESPONSE_reply_expired_denom_pub_hash ( - rc->connection, - &denom_pub_hash, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED, - "csr-withdraw"); - } - if (GNUNET_CRYPTO_BSA_CS != - dk->denom_pub.bsign_pub_key->cipher) - { - /* denomination is valid but not for CS */ - return TEH_RESPONSE_reply_invalid_denom_cipher_for_operation ( - rc->connection, - &denom_pub_hash); - } - } - - /* derive r_pub */ - { - enum TALER_ErrorCode ec; - const struct TEH_CsDeriveData cdd = { - .h_denom_pub = &denom_pub_hash, - .nonce = &nonce - }; - - ec = TEH_keys_denomination_cs_r_pub (&cdd, - false, - &ewv.details.cs_values); - if (TALER_EC_NONE != ec) - { - GNUNET_break (0); - return TALER_MHD_reply_with_ec (rc->connection, - ec, - NULL); - } - } - { - struct TALER_ExchangeBlindingValues exw = { - .blinding_inputs = &ewv - }; - - return TALER_MHD_REPLY_JSON_PACK ( - rc->connection, - MHD_HTTP_OK, - TALER_JSON_pack_exchange_blinding_values ("ewv", - &exw)); - } -} - - -/* end of taler-exchange-httpd_csr.c */ diff --git a/src/exchange/taler-exchange-httpd_csr.h b/src/exchange/taler-exchange-httpd_csr.h @@ -1,56 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2021 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 <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-exchange-httpd_csr.h - * @brief Handle /csr-* requests - * @author Lucien Heuzeveldt - * @author Gian Demarmles - */ -#ifndef TALER_EXCHANGE_HTTPD_CSR_H -#define TALER_EXCHANGE_HTTPD_CSR_H - -#include <microhttpd.h> -#include "taler-exchange-httpd.h" - - -/** - * Handle a "/csr-melt" request. - * - * @param rc request context - * @param root uploaded JSON data - * @param args empty array - * @return MHD result code - */ -MHD_RESULT -TEH_handler_csr_melt (struct TEH_RequestContext *rc, - const json_t *root, - const char *const args[]); - - -/** - * Handle a "/csr-withdraw" request. - * - * @param rc request context - * @param root uploaded JSON data - * @param args empty array - * @return MHD result code - */ -MHD_RESULT -TEH_handler_csr_withdraw (struct TEH_RequestContext *rc, - const json_t *root, - const char *const args[]); - -#endif diff --git a/src/exchange/taler-exchange-httpd_melt.c b/src/exchange/taler-exchange-httpd_melt.c @@ -1,488 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-exchange-httpd_melt.c - * @brief Handle melt requests - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - */ -#include "platform.h" -#include <gnunet/gnunet_util_lib.h> -#include <jansson.h> -#include <microhttpd.h> -#include "taler_json_lib.h" -#include "taler_mhd_lib.h" -#include "taler-exchange-httpd_mhd.h" -#include "taler-exchange-httpd_melt.h" -#include "taler-exchange-httpd_responses.h" -#include "taler-exchange-httpd_keys.h" -#include "taler_exchangedb_lib.h" - - -/** - * Send a response to a "melt" request. - * - * @param connection the connection to send the response to - * @param rc value the client committed to - * @param noreveal_index which index will the client not have to reveal - * @return a MHD status code - */ -static MHD_RESULT -reply_melt_success (struct MHD_Connection *connection, - const struct TALER_RefreshCommitmentP *rc, - uint32_t noreveal_index) -{ - struct TALER_ExchangePublicKeyP pub; - struct TALER_ExchangeSignatureP sig; - enum TALER_ErrorCode ec; - - if (TALER_EC_NONE != - (ec = TALER_exchange_online_melt_confirmation_sign ( - &TEH_keys_exchange_sign_, - rc, - noreveal_index, - &pub, - &sig))) - { - GNUNET_break (0); - return TALER_MHD_reply_with_ec (connection, - ec, - NULL); - } - return TALER_MHD_REPLY_JSON_PACK ( - connection, - MHD_HTTP_OK, - GNUNET_JSON_pack_uint64 ("noreveal_index", - noreveal_index), - GNUNET_JSON_pack_data_auto ("exchange_sig", - &sig), - GNUNET_JSON_pack_data_auto ("exchange_pub", - &pub)); -} - - -/** - * Context for the melt operation. - */ -struct MeltContext -{ - - /** - * noreveal_index is only initialized during - * #melt_transaction(). - */ - struct TALER_EXCHANGEDB_Refresh refresh_session; - - /** - * UUID of the coin in the known_coins table. - */ - uint64_t known_coin_id; - - /** - * Information about the @e coin's value. - */ - struct TALER_Amount coin_value; - - /** - * Information about the @e coin's refresh fee. - */ - struct TALER_Amount coin_refresh_fee; - - /** - * Refresh master secret, if any of the fresh denominations use CS. - */ - struct TALER_RefreshMasterSecretP rms; - - /** - * Set to true if this coin's denomination was revoked and the operation - * is thus only allowed for zombie coins where the transaction - * history includes a #TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP. - */ - bool zombie_required; - - /** - * We already checked and noticed that the coin is known. Hence we - * can skip the "ensure_coin_known" step of the transaction. - */ - bool coin_is_dirty; - - /** - * True if @e rms is missing. - */ - bool no_rms; -}; - - -/** - * Execute a "melt". We have been given a list of valid - * coins and a request to melt them into the given @a - * refresh_session_pub. Check that the coins all have the required - * value left and if so, store that they have been melted and confirm - * the melting operation to the client. - * - * 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 our `struct MeltContext` - * @param connection MHD request which triggered the transaction - * @param[out] mhd_ret set to MHD response status for @a connection, - * if transaction failed (!) - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -melt_transaction (void *cls, - struct MHD_Connection *connection, - MHD_RESULT *mhd_ret) -{ - struct MeltContext *rmc = cls; - enum GNUNET_DB_QueryStatus qs; - bool balance_ok; - - /* pick challenge and persist it */ - rmc->refresh_session.noreveal_index - = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG, - TALER_CNC_KAPPA); - - if (0 > - (qs = TEH_plugin->do_melt (TEH_plugin->cls, - rmc->no_rms - ? NULL - : &rmc->rms, - &rmc->refresh_session, - rmc->known_coin_id, - &rmc->zombie_required, - &balance_ok))) - { - if (GNUNET_DB_STATUS_SOFT_ERROR != qs) - { - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "melt"); - return GNUNET_DB_STATUS_HARD_ERROR; - } - return qs; - } - GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs); - if (rmc->zombie_required) - { - GNUNET_break_op (0); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_MELT_COIN_EXPIRED_NO_ZOMBIE, - NULL); - return GNUNET_DB_STATUS_HARD_ERROR; - } - if (! balance_ok) - { - GNUNET_break_op (0); - *mhd_ret - = TEH_RESPONSE_reply_coin_insufficient_funds ( - connection, - TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS, - &rmc->refresh_session.coin.denom_pub_hash, - &rmc->refresh_session.coin.coin_pub); - return GNUNET_DB_STATUS_HARD_ERROR; - } - /* All good, commit, final response will be generated by caller */ - TEH_METRICS_num_success[TEH_MT_SUCCESS_MELT]++; - return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; -} - - -/** - * Handle a "melt" request after the first parsing has - * happened. Performs the database transactions. - * - * @param connection the MHD connection to handle - * @param[in,out] rmc details about the melt request - * @return MHD result code - */ -static MHD_RESULT -database_melt (struct MHD_Connection *connection, - struct MeltContext *rmc) -{ - if (GNUNET_SYSERR == - TEH_plugin->preflight (TEH_plugin->cls)) - { - GNUNET_break (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_START_FAILED, - "preflight failure"); - } - - /* first, make sure coin is known */ - if (! rmc->coin_is_dirty) - { - MHD_RESULT mhd_ret = MHD_NO; - enum GNUNET_DB_QueryStatus qs; - - for (unsigned int tries = 0; tries<MAX_TRANSACTION_COMMIT_RETRIES; tries++) - { - qs = TEH_make_coin_known (&rmc->refresh_session.coin, - connection, - &rmc->known_coin_id, - &mhd_ret); - if (GNUNET_DB_STATUS_SOFT_ERROR != qs) - break; - } - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - { - GNUNET_break (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_COMMIT_FAILED, - "make_coin_known"); - } - if (qs < 0) - return mhd_ret; - } - - /* run main database transaction */ - { - MHD_RESULT mhd_ret; - - if (GNUNET_OK != - TEH_DB_run_transaction (connection, - "run melt", - TEH_MT_REQUEST_MELT, - &mhd_ret, - &melt_transaction, - rmc)) - return mhd_ret; - } - - /* Success. Generate ordinary response. */ - return reply_melt_success (connection, - &rmc->refresh_session.rc, - rmc->refresh_session.noreveal_index); -} - - -/** - * Check for information about the melted coin's denomination, - * extracting its validity status and fee structure. - * - * @param connection HTTP connection we are handling - * @param rmc parsed request information - * @return MHD status code - */ -static MHD_RESULT -check_melt_valid (struct MHD_Connection *connection, - struct MeltContext *rmc) -{ - /* Baseline: check if deposits/refreshes are generally - simply still allowed for this denomination */ - struct TEH_DenominationKey *dk; - MHD_RESULT mret; - - dk = TEH_keys_denomination_by_hash ( - &rmc->refresh_session.coin.denom_pub_hash, - connection, - &mret); - if (NULL == dk) - return mret; - - if (GNUNET_TIME_absolute_is_past (dk->meta.expire_legal.abs_time)) - { - /* Way too late now, even zombies have expired */ - return TEH_RESPONSE_reply_expired_denom_pub_hash ( - connection, - &rmc->refresh_session.coin.denom_pub_hash, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, - "MELT"); - } - - if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time)) - { - /* This denomination is not yet valid */ - return TEH_RESPONSE_reply_expired_denom_pub_hash ( - connection, - &rmc->refresh_session.coin.denom_pub_hash, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, - "MELT"); - } - - rmc->coin_refresh_fee = dk->meta.fees.refresh; - rmc->coin_value = dk->meta.value; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Melted coin's denomination is worth %s\n", - TALER_amount2s (&dk->meta.value)); - - /* sanity-check that "total melt amount > melt fee" */ - if (0 < - TALER_amount_cmp (&rmc->coin_refresh_fee, - &rmc->refresh_session.amount_with_fee)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_MELT_FEES_EXCEED_CONTRIBUTION, - NULL); - } - switch (dk->denom_pub.bsign_pub_key->cipher) - { - case GNUNET_CRYPTO_BSA_RSA: - TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_RSA]++; - break; - case GNUNET_CRYPTO_BSA_CS: - TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_CS]++; - break; - default: - break; - } - if (GNUNET_OK != - TALER_test_coin_valid (&rmc->refresh_session.coin, - &dk->denom_pub)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID, - NULL); - } - - /* verify signature of coin for melt operation */ - TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; - if (GNUNET_OK != - TALER_wallet_melt_verify (&rmc->refresh_session.amount_with_fee, - &rmc->coin_refresh_fee, - &rmc->refresh_session.rc, - &rmc->refresh_session.coin.denom_pub_hash, - &rmc->refresh_session.coin.h_age_commitment, - &rmc->refresh_session.coin.coin_pub, - &rmc->refresh_session.coin_sig)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_EXCHANGE_MELT_COIN_SIGNATURE_INVALID, - NULL); - } - - if (GNUNET_TIME_absolute_is_past (dk->meta.expire_deposit.abs_time)) - { - /* We are past deposit expiration time, but maybe this is a zombie? */ - struct TALER_DenominationHashP denom_hash; - enum GNUNET_DB_QueryStatus qs; - - /* Check that the coin is dirty (we have seen it before), as we will - not just allow melting of a *fresh* coin where the denomination was - revoked (those must be recouped) */ - qs = TEH_plugin->get_coin_denomination ( - TEH_plugin->cls, - &rmc->refresh_session.coin.coin_pub, - &rmc->known_coin_id, - &denom_hash); - if (0 > qs) - { - /* There is no good reason for a serialization failure here: */ - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "coin denomination"); - } - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) - { - /* We never saw this coin before, so _this_ justification is not OK */ - return TEH_RESPONSE_reply_expired_denom_pub_hash ( - connection, - &rmc->refresh_session.coin.denom_pub_hash, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, - "MELT"); - } - /* Minor optimization: no need to run the - "ensure_coin_known" part of the transaction */ - rmc->coin_is_dirty = true; - /* sanity check */ - if (0 != - GNUNET_memcmp (&denom_hash, - &rmc->refresh_session.coin.denom_pub_hash)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_ec ( - connection, - TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY, - TALER_B2S (&denom_hash)); - } - rmc->zombie_required = true; /* check later that zombie is satisfied */ - } - - return database_melt (connection, - rmc); -} - - -MHD_RESULT -TEH_handler_melt (struct MHD_Connection *connection, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - const json_t *root) -{ - struct MeltContext rmc; - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_denom_sig ("denom_sig", - &rmc.refresh_session.coin.denom_sig), - GNUNET_JSON_spec_fixed_auto ("denom_pub_hash", - &rmc.refresh_session.coin.denom_pub_hash), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_fixed_auto ("age_commitment_hash", - &rmc.refresh_session.coin.h_age_commitment), - &rmc.refresh_session.coin.no_age_commitment), - GNUNET_JSON_spec_fixed_auto ("confirm_sig", - &rmc.refresh_session.coin_sig), - TALER_JSON_spec_amount ("value_with_fee", - TEH_currency, - &rmc.refresh_session.amount_with_fee), - GNUNET_JSON_spec_fixed_auto ("rc", - &rmc.refresh_session.rc), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_fixed_auto ("rms", - &rmc.rms), - &rmc.no_rms), - GNUNET_JSON_spec_end () - }; - - memset (&rmc, 0, sizeof (rmc)); - rmc.refresh_session.coin.coin_pub = *coin_pub; - - { - enum GNUNET_GenericReturnValue ret; - ret = TALER_MHD_parse_json_data (connection, - root, - spec); - if (GNUNET_OK != ret) - return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES; - } - - { - MHD_RESULT res; - - res = check_melt_valid (connection, - &rmc); - GNUNET_JSON_parse_free (spec); - return res; - } -} - - -/* end of taler-exchange-httpd_melt.c */ diff --git a/src/exchange/taler-exchange-httpd_melt.h b/src/exchange/taler-exchange-httpd_melt.h @@ -1,48 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2017 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 <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-exchange-httpd_melt.h - * @brief Handle /coins/$COIN_PUB/melt requests - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - */ -#ifndef TALER_EXCHANGE_HTTPD_MELT_H -#define TALER_EXCHANGE_HTTPD_MELT_H - -#include <gnunet/gnunet_util_lib.h> -#include <microhttpd.h> -#include "taler-exchange-httpd.h" - - -/** - * Handle a "/coins/$COIN_PUB/melt" request. Parses the request into the JSON - * components and then hands things of to #check_melt_valid() to - * validate the melted coins, the signature and execute the melt using - * melt_transaction(). - * - * @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_melt (struct MHD_Connection *connection, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - const json_t *root); - - -#endif diff --git a/src/exchange/taler-exchange-httpd_refreshes_reveal.c b/src/exchange/taler-exchange-httpd_refreshes_reveal.c @@ -1,1076 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-exchange-httpd_refreshes_reveal.c - * @brief Handle /refreshes/$RCH/reveal requests - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - */ -#include "platform.h" -#include <gnunet/gnunet_util_lib.h> -#include <jansson.h> -#include <microhttpd.h> -#include "taler_mhd_lib.h" -#include "taler-exchange-httpd_mhd.h" -#include "taler-exchange-httpd_refreshes_reveal.h" -#include "taler-exchange-httpd_responses.h" -#include "taler-exchange-httpd_keys.h" - - -/** - * Send a response for "/refreshes/$RCH/reveal". - * - * @param connection the connection to send the response to - * @param num_freshcoins number of new coins for which we reveal data - * @param rrcs array of @a num_freshcoins signatures revealed - * @return a MHD result code - */ -static MHD_RESULT -reply_refreshes_reveal_success ( - struct MHD_Connection *connection, - unsigned int num_freshcoins, - const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs) -{ - json_t *list; - - list = json_array (); - GNUNET_assert (NULL != list); - for (unsigned int freshcoin_index = 0; - freshcoin_index < num_freshcoins; - freshcoin_index++) - { - json_t *obj; - - obj = GNUNET_JSON_PACK ( - TALER_JSON_pack_blinded_denom_sig ("ev_sig", - &rrcs[freshcoin_index].coin_sig)); - GNUNET_assert (0 == - json_array_append_new (list, - obj)); - } - - return TALER_MHD_REPLY_JSON_PACK ( - connection, - MHD_HTTP_OK, - GNUNET_JSON_pack_array_steal ("ev_sigs", - list)); -} - - -/** - * State for a /refreshes/$RCH/reveal operation. - */ -struct RevealContext -{ - - /** - * Commitment of the refresh operation. - */ - struct TALER_RefreshCommitmentP rc; - - /** - * Transfer public key at gamma. - */ - struct TALER_TransferPublicKeyP gamma_tp; - - /** - * Transfer private keys revealed to us. - */ - struct TALER_TransferPrivateKeyP transfer_privs[TALER_CNC_KAPPA - 1]; - - /** - * Melt data for our session we got from the database for @e rc. - */ - struct TALER_EXCHANGEDB_Melt melt; - - /** - * Denominations being requested. - */ - const struct TEH_DenominationKey **dks; - - /** - * Age commitment that was used for the original coin. If not NULL, its hash - * should be the same as melt.session.h_age_commitment. - */ - struct TALER_AgeCommitment *old_age_commitment; - - /** - * Array of information about fresh coins being revealed. - */ - struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs; - - /** - * Envelopes to be signed. - */ - struct TALER_RefreshCoinData *rcds; - - /** - * Refresh master secret. - */ - struct TALER_RefreshMasterSecretP rms; - - /** - * Size of the @e dks, @e rcds and @e ev_sigs arrays (if non-NULL). - */ - unsigned int num_fresh_coins; - - /** - * True if @e rms was not provided. - */ - bool no_rms; -}; - - -/** - * Check client's revelation against the original commitment. - * The client is revealing to us the - * transfer keys for @a #TALER_CNC_KAPPA-1 sets of coins. Verify that the - * revealed transfer keys would allow linkage to the blinded coins. - * - * IF it returns #GNUNET_OK, the transaction logic MUST - * NOT queue a MHD response. IF it returns an error, the - * transaction logic MUST queue a MHD response and set @a mhd_ret. - * - * @param rctx our operation context - * @param connection MHD request which triggered the transaction - * @param[out] mhd_ret set to MHD response status for @a connection, - * if transaction failed (!) - * @return #GNUNET_OK if commitment was OK - */ -static enum GNUNET_GenericReturnValue -check_commitment (struct RevealContext *rctx, - struct MHD_Connection *connection, - MHD_RESULT *mhd_ret) -{ - const union GNUNET_CRYPTO_BlindSessionNonce *nonces[rctx->num_fresh_coins]; - - memset (nonces, - 0, - sizeof (nonces)); - for (unsigned int j = 0; j<rctx->num_fresh_coins; j++) - { - const struct TALER_DenominationPublicKey *dk = &rctx->dks[j]->denom_pub; - - if (dk->bsign_pub_key->cipher != - rctx->rcds[j].blinded_planchet.blinded_message->cipher) - { - GNUNET_break (0); - *mhd_ret = TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH, - NULL); - return GNUNET_SYSERR; - } - switch (dk->bsign_pub_key->cipher) - { - case GNUNET_CRYPTO_BSA_INVALID: - GNUNET_break (0); - *mhd_ret = TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - NULL); - return GNUNET_SYSERR; - case GNUNET_CRYPTO_BSA_RSA: - continue; - case GNUNET_CRYPTO_BSA_CS: - nonces[j] - = (const union GNUNET_CRYPTO_BlindSessionNonce *) - &rctx->rcds[j].blinded_planchet.blinded_message->details. - cs_blinded_message.nonce; - break; - } - } - - // OPTIMIZE: do this in batch later! - for (unsigned int j = 0; j<rctx->num_fresh_coins; j++) - { - const struct TALER_DenominationPublicKey *dk = &rctx->dks[j]->denom_pub; - struct TALER_ExchangeBlindingValues *alg_values - = &rctx->rrcs[j].exchange_vals; - struct GNUNET_CRYPTO_BlindingInputValues *bi; - - bi = GNUNET_new (struct GNUNET_CRYPTO_BlindingInputValues); - alg_values->blinding_inputs = bi; - bi->rc = 1; - bi->cipher = dk->bsign_pub_key->cipher; - switch (dk->bsign_pub_key->cipher) - { - case GNUNET_CRYPTO_BSA_INVALID: - GNUNET_assert (0); - return GNUNET_SYSERR; - case GNUNET_CRYPTO_BSA_RSA: - continue; - case GNUNET_CRYPTO_BSA_CS: - { - enum TALER_ErrorCode ec; - const struct TEH_CsDeriveData cdd = { - .h_denom_pub = &rctx->rrcs[j].h_denom_pub, - .nonce = &nonces[j]->cs_nonce - }; - - ec = TEH_keys_denomination_cs_r_pub ( - &cdd, - true, - &bi->details.cs_values); - if (TALER_EC_NONE != ec) - { - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - ec, - NULL); - return GNUNET_SYSERR; - } - } - } - } - /* Verify commitment */ - { - /* Note that the contents of rcs[melt.session.noreveal_index] - will be aliased and are *not* allocated (or deallocated) in - this function -- in contrast to the other offsets! */ - struct TALER_RefreshCommitmentEntry rcs[TALER_CNC_KAPPA]; - struct TALER_RefreshCommitmentP rc_expected; - unsigned int off; - - off = 0; /* did we pass session.noreveal_index yet? */ - for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++) - { - struct TALER_RefreshCommitmentEntry *rce = &rcs[i]; - - if (i == rctx->melt.session.noreveal_index) - { - /* Take these coin envelopes from the client */ - rce->transfer_pub = rctx->gamma_tp; - rce->new_coins = rctx->rcds; - off = 1; - } - else - { - /* Reconstruct coin envelopes from transfer private key */ - const struct TALER_TransferPrivateKeyP *tpriv - = &rctx->transfer_privs[i - off]; - struct TALER_TransferSecretP ts; - struct TALER_AgeCommitmentHash h = {0}; - struct TALER_AgeCommitmentHash *hac = NULL; - - GNUNET_CRYPTO_ecdhe_key_get_public (&tpriv->ecdhe_priv, - &rce->transfer_pub.ecdhe_pub); - TEH_METRICS_num_keyexchanges[TEH_MT_KEYX_ECDH]++; - TALER_link_reveal_transfer_secret (tpriv, - &rctx->melt.session.coin.coin_pub, - &ts); - rce->new_coins = GNUNET_new_array (rctx->num_fresh_coins, - struct TALER_RefreshCoinData); - for (unsigned int j = 0; j<rctx->num_fresh_coins; j++) - { - struct TALER_RefreshCoinData *rcd = &rce->new_coins[j]; - struct TALER_CoinSpendPrivateKeyP coin_priv; - union GNUNET_CRYPTO_BlindingSecretP bks; - const struct TALER_ExchangeBlindingValues *alg_value - = &rctx->rrcs[j].exchange_vals; - struct TALER_PlanchetDetail pd = {0}; - struct TALER_CoinPubHashP c_hash; - struct TALER_PlanchetMasterSecretP ps; - - rcd->dk = &rctx->dks[j]->denom_pub; - TALER_transfer_secret_to_planchet_secret (&ts, - j, - &ps); - TALER_planchet_setup_coin_priv (&ps, - alg_value, - &coin_priv); - TALER_planchet_blinding_secret_create (&ps, - alg_value, - &bks); - /* Calculate, if applicable, the age commitment and its hash, from - * the transfer_secret and the old age commitment. */ - if (NULL != rctx->old_age_commitment) - { - struct TALER_AgeCommitmentProof acp = { - /* we only need the commitment, not the proof, for the call to - * TALER_age_commitment_derive. */ - .commitment = *(rctx->old_age_commitment) - }; - struct TALER_AgeCommitmentProof nacp = {0}; - - GNUNET_assert (GNUNET_OK == - TALER_age_commitment_proof_derive ( - &acp, - &ts.key, - &nacp)); - TALER_age_commitment_hash (&nacp.commitment, - &h); - TALER_age_commitment_proof_free (&nacp); - hac = &h; - } - - GNUNET_assert (GNUNET_OK == - TALER_planchet_prepare (rcd->dk, - alg_value, - &bks, - nonces[j], - &coin_priv, - hac, - &c_hash, - &pd)); - rcd->blinded_planchet = pd.blinded_planchet; - } - } - } - TALER_refresh_get_commitment (&rc_expected, - TALER_CNC_KAPPA, - rctx->no_rms - ? NULL - : &rctx->rms, - rctx->num_fresh_coins, - rcs, - &rctx->melt.session.coin.coin_pub, - &rctx->melt.session.amount_with_fee); - - /* Free resources allocated above */ - for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++) - { - struct TALER_RefreshCommitmentEntry *rce = &rcs[i]; - - if (i == rctx->melt.session.noreveal_index) - continue; /* This offset is special: not allocated! */ - for (unsigned int j = 0; j<rctx->num_fresh_coins; j++) - { - struct TALER_RefreshCoinData *rcd = &rce->new_coins[j]; - - TALER_blinded_planchet_free (&rcd->blinded_planchet); - } - GNUNET_free (rce->new_coins); - } - - /* Verify rc_expected matches rc */ - if (0 != GNUNET_memcmp (&rctx->rc, - &rc_expected)) - { - GNUNET_break_op (0); - *mhd_ret = TALER_MHD_REPLY_JSON_PACK ( - connection, - MHD_HTTP_CONFLICT, - TALER_JSON_pack_ec ( - TALER_EC_EXCHANGE_REFRESHES_REVEAL_COMMITMENT_VIOLATION), - GNUNET_JSON_pack_data_auto ("rc_expected", - &rc_expected)); - return GNUNET_SYSERR; - } - } /* end of checking "rc_expected" */ - - /* check amounts add up! */ - { - struct TALER_Amount refresh_cost; - - refresh_cost = rctx->melt.melt_fee; - for (unsigned int i = 0; i<rctx->num_fresh_coins; i++) - { - struct TALER_Amount total; - - if ( (0 > - TALER_amount_add (&total, - &rctx->dks[i]->meta.fees.withdraw, - &rctx->dks[i]->meta.value)) || - (0 > - TALER_amount_add (&refresh_cost, - &refresh_cost, - &total)) ) - { - GNUNET_break_op (0); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_REFRESHES_REVEAL_COST_CALCULATION_OVERFLOW, - NULL); - return GNUNET_SYSERR; - } - } - if (0 < TALER_amount_cmp (&refresh_cost, - &rctx->melt.session.amount_with_fee)) - { - GNUNET_break_op (0); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_REFRESHES_REVEAL_AMOUNT_INSUFFICIENT, - NULL); - return GNUNET_SYSERR; - } - } - return GNUNET_OK; -} - - -/** - * Resolve denomination hashes. - * - * @param connection the MHD connection to handle - * @param rctx context for the operation, partially built at this time - * @param link_sigs_json link signatures in JSON format - * @param new_denoms_h_json requests for fresh coins to be created - * @param old_age_commitment_json age commitment that went into the withdrawal, maybe NULL - * @param coin_evs envelopes of gamma-selected coins to be signed - * @return MHD result code - */ -static MHD_RESULT -resolve_refreshes_reveal_denominations ( - struct MHD_Connection *connection, - struct RevealContext *rctx, - const json_t *link_sigs_json, - const json_t *new_denoms_h_json, - const json_t *old_age_commitment_json, - const json_t *coin_evs) -{ - unsigned int num_fresh_coins = json_array_size (new_denoms_h_json); - /* We know num_fresh_coins is bounded by #TALER_MAX_COINS, so this is safe */ - const struct TEH_DenominationKey *dks[num_fresh_coins]; - const struct TEH_DenominationKey *old_dk; - struct TALER_RefreshCoinData rcds[num_fresh_coins]; - struct TALER_EXCHANGEDB_RefreshRevealedCoin rrcs[num_fresh_coins]; - MHD_RESULT ret; - struct TEH_KeyStateHandle *ksh; - uint64_t melt_serial_id; - - memset (dks, 0, sizeof (dks)); - memset (rrcs, 0, sizeof (rrcs)); - memset (rcds, 0, sizeof (rcds)); - rctx->num_fresh_coins = num_fresh_coins; - - ksh = TEH_keys_get_state (); - if (NULL == ksh) - { - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, - NULL); - } - - /* lookup old_coin_pub in database */ - { - enum GNUNET_DB_QueryStatus qs; - - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - (qs = TEH_plugin->get_melt (TEH_plugin->cls, - &rctx->rc, - &rctx->melt, - &melt_serial_id))) - { - switch (qs) - { - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_EXCHANGE_REFRESHES_REVEAL_SESSION_UNKNOWN, - NULL); - break; - case GNUNET_DB_STATUS_HARD_ERROR: - ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "melt"); - break; - case GNUNET_DB_STATUS_SOFT_ERROR: - default: - GNUNET_break (0); /* should be impossible */ - ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - NULL); - break; - } - goto cleanup; - } - if (rctx->melt.session.noreveal_index >= TALER_CNC_KAPPA) - { - GNUNET_break (0); - ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "melt"); - goto cleanup; - } - } - - old_dk = TEH_keys_denomination_by_hash_from_state ( - ksh, - &rctx->melt.session.coin.denom_pub_hash, - connection, - &ret); - if (NULL == old_dk) - return ret; - - /* Parse denomination key hashes */ - for (unsigned int i = 0; i<num_fresh_coins; i++) - { - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto (NULL, - &rrcs[i].h_denom_pub), - GNUNET_JSON_spec_end () - }; - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_array (connection, - new_denoms_h_json, - spec, - i, - -1); - if (GNUNET_OK != res) - return (GNUNET_NO == res) ? MHD_YES : MHD_NO; - dks[i] = TEH_keys_denomination_by_hash_from_state (ksh, - &rrcs[i].h_denom_pub, - connection, - &ret); - if (NULL == dks[i]) - return ret; - if ( (GNUNET_CRYPTO_BSA_CS == - dks[i]->denom_pub.bsign_pub_key->cipher) && - (rctx->no_rms) ) - { - return TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MISSING, - "rms"); - } - if (GNUNET_TIME_absolute_is_past (dks[i]->meta.expire_withdraw.abs_time)) - { - /* This denomination is past the expiration time for withdraws */ - return TEH_RESPONSE_reply_expired_denom_pub_hash ( - connection, - &rrcs[i].h_denom_pub, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, - "REVEAL"); - } - if (GNUNET_TIME_absolute_is_future (dks[i]->meta.start.abs_time)) - { - /* This denomination is not yet valid */ - return TEH_RESPONSE_reply_expired_denom_pub_hash ( - connection, - &rrcs[i].h_denom_pub, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, - "REVEAL"); - } - if (dks[i]->recoup_possible) - { - /* This denomination has been revoked */ - return TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_GONE, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED, - NULL); - } - } - - /* Parse coin envelopes */ - for (unsigned int i = 0; i<num_fresh_coins; i++) - { - struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i]; - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_blinded_planchet (NULL, - &rrc->blinded_planchet), - GNUNET_JSON_spec_end () - }; - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_array (connection, - coin_evs, - spec, - i, - -1); - if (GNUNET_OK != res) - { - for (unsigned int j = 0; j<i; j++) - TALER_blinded_planchet_free (&rrcs[j].blinded_planchet); - return (GNUNET_NO == res) ? MHD_YES : MHD_NO; - } - TALER_coin_ev_hash (&rrc->blinded_planchet, - &rrcs[i].h_denom_pub, - &rrc->coin_envelope_hash); - } - - if (TEH_age_restriction_enabled && - ((NULL == old_age_commitment_json) != - TALER_AgeCommitmentHash_isNullOrZero ( - &rctx->melt.session.coin.h_age_commitment))) - { - GNUNET_break (0); - return MHD_NO; - } - - /* Reconstruct the old age commitment and verify its hash matches the one - * from the melt request */ - if (TEH_age_restriction_enabled && - (NULL != old_age_commitment_json)) - { - enum GNUNET_GenericReturnValue res; - struct TALER_AgeCommitment *oac; - size_t ng = json_array_size (old_age_commitment_json); - bool failed = true; - - /* Has been checked in handle_refreshes_reveal_json() */ - GNUNET_assert (ng == TEH_age_restriction_config.num_groups); - - rctx->old_age_commitment = GNUNET_new (struct TALER_AgeCommitment); - oac = rctx->old_age_commitment; - oac->mask = old_dk->meta.age_mask; - oac->num = ng; - oac->pubs = GNUNET_new_array (ng, struct TALER_AgeCommitmentPublicKeyP); - - /* Extract old age commitment */ - for (unsigned int i = 0; i< ng; i++) - { - struct GNUNET_JSON_Specification ac_spec[] = { - GNUNET_JSON_spec_fixed_auto (NULL, - &oac->pubs[i]), - GNUNET_JSON_spec_end () - }; - - res = TALER_MHD_parse_json_array (connection, - old_age_commitment_json, - ac_spec, - i, - -1); - - GNUNET_break_op (GNUNET_OK == res); - if (GNUNET_OK != res) - goto clean_age; - } - - /* Sanity check: Compare hash from melting with hash of this age commitment */ - { - struct TALER_AgeCommitmentHash hac = {0}; - TALER_age_commitment_hash (oac, &hac); - if (0 != memcmp (&hac, - &rctx->melt.session.coin.h_age_commitment, - sizeof(struct TALER_AgeCommitmentHash))) - goto clean_age; - } - - failed = false; - -clean_age: - if (failed) - { - TALER_age_commitment_free (oac); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_REFRESHES_REVEAL_AGE_RESTRICTION_COMMITMENT_INVALID, - "old_age_commitment"); - } - } - - /* Parse link signatures array */ - for (unsigned int i = 0; i<num_fresh_coins; i++) - { - struct GNUNET_JSON_Specification link_spec[] = { - GNUNET_JSON_spec_fixed_auto (NULL, - &rrcs[i].orig_coin_link_sig), - GNUNET_JSON_spec_end () - }; - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_array (connection, - link_sigs_json, - link_spec, - i, - -1); - if (GNUNET_OK != res) - return (GNUNET_NO == res) ? MHD_YES : MHD_NO; - - /* Check signature */ - TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; - if (GNUNET_OK != - TALER_wallet_link_verify ( - &rrcs[i].h_denom_pub, - &rctx->gamma_tp, - &rrcs[i].coin_envelope_hash, - &rctx->melt.session.coin.coin_pub, - &rrcs[i].orig_coin_link_sig)) - { - GNUNET_break_op (0); - ret = TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_EXCHANGE_REFRESHES_REVEAL_LINK_SIGNATURE_INVALID, - NULL); - goto cleanup; - } - } - - /* prepare for check_commitment */ - for (unsigned int i = 0; i<rctx->num_fresh_coins; i++) - { - const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i]; - struct TALER_RefreshCoinData *rcd = &rcds[i]; - - rcd->blinded_planchet = rrc->blinded_planchet; - rcd->dk = &dks[i]->denom_pub; - if (rcd->blinded_planchet.blinded_message->cipher != - rcd->dk->bsign_pub_key->cipher) - { - GNUNET_break_op (0); - ret = TALER_MHD_REPLY_JSON_PACK ( - connection, - MHD_HTTP_BAD_REQUEST, - TALER_JSON_pack_ec ( - TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH)); - goto cleanup; - } - } - - rctx->dks = dks; - rctx->rcds = rcds; - rctx->rrcs = rrcs; - if (GNUNET_OK != - check_commitment (rctx, - connection, - &ret)) - goto cleanup; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Creating %u signatures\n", - (unsigned int) rctx->num_fresh_coins); - - /* create fresh coin signatures */ - { - struct TEH_CoinSignData csds[rctx->num_fresh_coins]; - struct TALER_BlindedDenominationSignature bss[rctx->num_fresh_coins]; - enum TALER_ErrorCode ec; - - for (unsigned int i = 0; i<rctx->num_fresh_coins; i++) - { - csds[i].h_denom_pub = &rrcs[i].h_denom_pub; - csds[i].bp = &rcds[i].blinded_planchet; - } - ec = TEH_keys_denomination_batch_sign ( - rctx->num_fresh_coins, - csds, - true, - bss); - if (TALER_EC_NONE != ec) - { - GNUNET_break (0); - ret = TALER_MHD_reply_with_ec (connection, - ec, - NULL); - goto cleanup; - } - - for (unsigned int i = 0; i<rctx->num_fresh_coins; i++) - { - rrcs[i].coin_sig = bss[i]; - rrcs[i].blinded_planchet = rcds[i].blinded_planchet; - } - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Signatures ready, starting DB interaction\n"); - - { - enum GNUNET_DB_QueryStatus qs; - - for (unsigned int r = 0; r<MAX_TRANSACTION_COMMIT_RETRIES; r++) - { - bool changed; - - /* Persist operation result in DB */ - if (GNUNET_OK != - TEH_plugin->start (TEH_plugin->cls, - "insert_refresh_reveal batch")) - { - GNUNET_break (0); - ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_START_FAILED, - NULL); - goto cleanup; - } - - qs = TEH_plugin->insert_refresh_reveal ( - TEH_plugin->cls, - melt_serial_id, - num_fresh_coins, - rrcs, - TALER_CNC_KAPPA - 1, - rctx->transfer_privs, - &rctx->gamma_tp); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - { - TEH_plugin->rollback (TEH_plugin->cls); - continue; - } - /* 0 == qs is ok, as we did not check for repeated requests */ - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - { - GNUNET_break (0); - TEH_plugin->rollback (TEH_plugin->cls); - ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "insert_refresh_reveal"); - goto cleanup; - } - changed = (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs); - qs = TEH_plugin->commit (TEH_plugin->cls); - if (qs >= 0) - { - if (changed) - TEH_METRICS_num_success[TEH_MT_SUCCESS_REFRESH_REVEAL]++; - break; /* success */ - } - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - { - GNUNET_break (0); - TEH_plugin->rollback (TEH_plugin->cls); - ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_COMMIT_FAILED, - NULL); - goto cleanup; - } - TEH_plugin->rollback (TEH_plugin->cls); - } - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - { - GNUNET_break (0); - TEH_plugin->rollback (TEH_plugin->cls); - ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_SOFT_FAILURE, - NULL); - goto cleanup; - } - } - /* Generate final (positive) response */ - ret = reply_refreshes_reveal_success (connection, - num_fresh_coins, - rrcs); -cleanup: - GNUNET_break (MHD_NO != ret); - /* free resources */ - for (unsigned int i = 0; i<num_fresh_coins; i++) - { - struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i]; - struct TALER_ExchangeBlindingValues *alg_values - = &rrcs[i].exchange_vals; - - GNUNET_free (alg_values->blinding_inputs); - TALER_blinded_denom_sig_free (&rrc->coin_sig); - TALER_blinded_planchet_free (&rrc->blinded_planchet); - } - return ret; -} - - -/** - * Handle a "/refreshes/$RCH/reveal" request. Parses the given JSON - * transfer private keys and if successful, passes everything to - * #resolve_refreshes_reveal_denominations() which will verify that the - * revealed information is valid then returns the signed refreshed - * coins. - * - * If the denomination has age restriction support, the array of EDDSA public - * keys, one for each age group that was activated during the withdrawal - * by the parent/ward, must be provided in old_age_commitment. The hash of - * this array must be the same as the h_age_commitment of the persisted reveal - * request. - * - * @param connection the MHD connection to handle - * @param rctx context for the operation, partially built at this time - * @param tp_json private transfer keys in JSON format - * @param link_sigs_json link signatures in JSON format - * @param new_denoms_h_json requests for fresh coins to be created - * @param old_age_commitment_json array of EDDSA public keys in JSON, used for age restriction, maybe NULL - * @param coin_evs envelopes of gamma-selected coins to be signed - * @return MHD result code - */ -static MHD_RESULT -handle_refreshes_reveal_json (struct MHD_Connection *connection, - struct RevealContext *rctx, - const json_t *tp_json, - const json_t *link_sigs_json, - const json_t *new_denoms_h_json, - const json_t *old_age_commitment_json, - const json_t *coin_evs) -{ - unsigned int num_fresh_coins = json_array_size (new_denoms_h_json); - unsigned int num_tprivs = json_array_size (tp_json); - - GNUNET_assert (num_tprivs == TALER_CNC_KAPPA - 1); /* checked just earlier */ - if ( (num_fresh_coins >= TALER_MAX_COINS) || - (0 == num_fresh_coins) ) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_GENERIC_NEW_DENOMS_ARRAY_SIZE_EXCESSIVE, - NULL); - - } - if (json_array_size (new_denoms_h_json) != - json_array_size (coin_evs)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_REFRESHES_REVEAL_NEW_DENOMS_ARRAY_SIZE_MISMATCH, - "new_denoms/coin_evs"); - } - if (json_array_size (new_denoms_h_json) != - json_array_size (link_sigs_json)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_REFRESHES_REVEAL_NEW_DENOMS_ARRAY_SIZE_MISMATCH, - "new_denoms/link_sigs"); - } - - /* Sanity check of age commitment: If it was provided, it _must_ be an array - * of the size the # of age groups */ - if (NULL != old_age_commitment_json - && TEH_age_restriction_config.num_groups != - json_array_size (old_age_commitment_json)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_REFRESHES_REVEAL_AGE_RESTRICTION_COMMITMENT_INVALID, - "old_age_commitment"); - } - - /* Parse transfer private keys array */ - for (unsigned int i = 0; i<num_tprivs; i++) - { - struct GNUNET_JSON_Specification trans_spec[] = { - GNUNET_JSON_spec_fixed_auto (NULL, - &rctx->transfer_privs[i]), - GNUNET_JSON_spec_end () - }; - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_array (connection, - tp_json, - trans_spec, - i, - -1); - if (GNUNET_OK != res) - return (GNUNET_NO == res) ? MHD_YES : MHD_NO; - } - - return resolve_refreshes_reveal_denominations (connection, - rctx, - link_sigs_json, - new_denoms_h_json, - old_age_commitment_json, - coin_evs); -} - - -MHD_RESULT -TEH_handler_reveal (struct TEH_RequestContext *rc, - const json_t *root, - const char *const args[2]) -{ - const json_t *coin_evs; - const json_t *transfer_privs; - const json_t *link_sigs; - const json_t *new_denoms_h; - const json_t *old_age_commitment; - struct RevealContext rctx; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("transfer_pub", - &rctx.gamma_tp), - GNUNET_JSON_spec_array_const ("transfer_privs", - &transfer_privs), - GNUNET_JSON_spec_array_const ("link_sigs", - &link_sigs), - GNUNET_JSON_spec_array_const ("coin_evs", - &coin_evs), - GNUNET_JSON_spec_array_const ("new_denoms_h", - &new_denoms_h), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_array_const ("old_age_commitment", - &old_age_commitment), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_fixed_auto ("rms", - &rctx.rms), - &rctx.no_rms), - GNUNET_JSON_spec_end () - }; - - memset (&rctx, - 0, - sizeof (rctx)); - if (GNUNET_OK != - GNUNET_STRINGS_string_to_data (args[0], - strlen (args[0]), - &rctx.rc, - sizeof (rctx.rc))) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_REFRESHES_REVEAL_INVALID_RCH, - args[0]); - } - if (0 != strcmp (args[1], - "reveal")) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_REFRESHES_REVEAL_OPERATION_INVALID, - args[1]); - } - - { - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_data (rc->connection, - root, - spec); - if (GNUNET_OK != res) - { - GNUNET_break_op (0); - return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; - } - } - - /* Check we got enough transfer private keys */ - /* Note we do +1 as 1 row (cut-and-choose!) is missing! */ - if (TALER_CNC_KAPPA != json_array_size (transfer_privs) + 1) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_REFRESHES_REVEAL_CNC_TRANSFER_ARRAY_SIZE_INVALID, - NULL); - } - - return handle_refreshes_reveal_json (rc->connection, - &rctx, - transfer_privs, - link_sigs, - new_denoms_h, - old_age_commitment, - coin_evs); -} - - -/* end of taler-exchange-httpd_refreshes_reveal.c */ diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am @@ -208,7 +208,6 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \ pg_get_global_fees.h pg_get_global_fees.c \ pg_get_known_coin.h pg_get_known_coin.c \ pg_get_kyc_rules.h pg_get_kyc_rules.c \ - pg_get_melt.h pg_get_melt.c \ pg_get_refresh.h pg_get_refresh.c \ pg_get_old_coin_by_h_blind.h pg_get_old_coin_by_h_blind.c \ pg_get_pending_kyc_requirement_process.h pg_get_pending_kyc_requirement_process.c \ @@ -247,7 +246,6 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \ pg_insert_partner.h pg_insert_partner.c \ pg_insert_purse_request.h pg_insert_purse_request.c \ pg_insert_records_by_table.c pg_insert_records_by_table.h \ - pg_insert_refresh_reveal.h pg_insert_refresh_reveal.c \ pg_insert_refund.h pg_insert_refund.c \ pg_insert_reserve_closed.h pg_insert_reserve_closed.c \ pg_insert_reserve_open_deposit.c pg_insert_reserve_open_deposit.h \ diff --git a/src/exchangedb/perf_get_link_data.c b/src/exchangedb/perf_get_link_data.c @@ -1,546 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-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 <http://www.gnu.org/licenses/> -*/ -/** - * @file exchangedb/perf_get_link_data.c - * @brief benchmark for get_link_data - * @author Joseph Xu - */ -#include "platform.h" -#include "taler_exchangedb_lib.h" -#include "taler_json_lib.h" -#include "taler_exchangedb_plugin.h" -#include "math.h" - -/** - * Report line of error if @a cond is true, and jump to label "drop". - */ -#define FAILIF(cond) \ - do { \ - if (! (cond)) {break;} \ - GNUNET_break (0); \ - goto drop; \ - } while (0) - - -/** - * Initializes @a ptr with random data. - */ -#define RND_BLK(ptr) \ - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, ptr, sizeof (* \ - ptr)) - -/** - * Initializes @a ptr with zeros. - */ -#define ZR_BLK(ptr) \ - memset (ptr, 0, sizeof (*ptr)) - -#define CURRENCY "EUR" -#define RSA_KEY_SIZE 1024 -#define ROUNDS 100 -#define NUM_ROWS 1000 -#define MELT_NEW_COINS 5 -#define DENOMINATIONS 5 -#define MELT_NOREVEAL_INDEX 1 -/** - * Database plugin under test. - */ -static struct TALER_EXCHANGEDB_Plugin *plugin; -/** - * Denomination keys used for fresh coins in melt test. - */ -static struct DenomKeyPair **new_dkp; -static int result; - -static struct TALER_TransferPrivateKeyP tprivs[TALER_CNC_KAPPA]; -static struct TALER_TransferPublicKeyP tpub; -struct DenomKeyPair -{ - struct TALER_DenominationPrivateKey priv; - struct TALER_DenominationPublicKey pub; -}; - - -/** - * Destroy a denomination key pair. The key is not necessarily removed from the DB. - * - * @param dkp the key pair to destroy - */ -static void -destroy_denom_key_pair (struct DenomKeyPair *dkp) -{ - TALER_denom_pub_free (&dkp->pub); - TALER_denom_priv_free (&dkp->priv); - GNUNET_free (dkp); -} - - -/** - * Create a denomination key pair by registering the denomination in the DB. - * - * @param size the size of the denomination key - * @param now time to use for key generation, legal expiration will be 3h later. - * @param fees fees to use - * @return the denominaiton key pair; NULL upon error - */ -static struct DenomKeyPair * -create_denom_key_pair (unsigned int size, - struct GNUNET_TIME_Timestamp now, - const struct TALER_Amount *value, - const struct TALER_DenomFeeSet *fees) -{ - struct DenomKeyPair *dkp; - struct TALER_EXCHANGEDB_DenominationKey dki; - struct TALER_EXCHANGEDB_DenominationKeyInformation issue2; - - dkp = GNUNET_new (struct DenomKeyPair); - GNUNET_assert (GNUNET_OK == - TALER_denom_priv_create (&dkp->priv, - &dkp->pub, - GNUNET_CRYPTO_BSA_RSA, - size)); - memset (&dki, - 0, - sizeof (struct TALER_EXCHANGEDB_DenominationKey)); - dki.denom_pub = dkp->pub; - dki.issue.start = now; - dki.issue.expire_withdraw - = GNUNET_TIME_absolute_to_timestamp ( - GNUNET_TIME_absolute_add ( - now.abs_time, - GNUNET_TIME_UNIT_HOURS)); - dki.issue.expire_deposit - = GNUNET_TIME_absolute_to_timestamp ( - GNUNET_TIME_absolute_add ( - now.abs_time, - GNUNET_TIME_relative_multiply ( - GNUNET_TIME_UNIT_HOURS, 2))); - dki.issue.expire_legal - = GNUNET_TIME_absolute_to_timestamp ( - GNUNET_TIME_absolute_add ( - now.abs_time, - GNUNET_TIME_relative_multiply ( - GNUNET_TIME_UNIT_HOURS, 3))); - dki.issue.value = *value; - dki.issue.fees = *fees; - TALER_denom_pub_hash (&dkp->pub, - &dki.issue.denom_hash); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->insert_denomination_info (plugin->cls, - &dki.denom_pub, - &dki.issue)) - { - GNUNET_break (0); - destroy_denom_key_pair (dkp); - return NULL; - } - memset (&issue2, 0, sizeof (issue2)); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->get_denomination_info (plugin->cls, - &dki.issue.denom_hash, - NULL, - &issue2)) - { - GNUNET_break (0); - destroy_denom_key_pair (dkp); - return NULL; - } - if (0 != GNUNET_memcmp (&dki.issue, - &issue2)) - { - GNUNET_break (0); - destroy_denom_key_pair (dkp); - return NULL; - } - return dkp; -} - - -/** - * Function called with the session hashes and transfer secret - * information for a given coin. - * - * @param cls closure - * @param transfer_pub public transfer key for the session - * @param ldl link data for @a transfer_pub - */ -static void -handle_link_data_cb (void *cls, - const struct TALER_TransferPublicKeyP *transfer_pub, - const struct TALER_EXCHANGEDB_LinkList *ldl) -{ - (void) cls; - (void) transfer_pub; - (void) ldl; -} - - -/** - * Main function that will be run by the scheduler. - * - * @param cls closure with config - */ - -static void -run (void *cls) -{ - struct TALER_EXCHANGEDB_Refresh *refresh; - uint64_t melt_serial_id; - struct GNUNET_CONFIGURATION_Handle *cfg = cls; - const uint32_t num_partitions = 10; - struct DenomKeyPair *dkp = NULL; - struct TALER_EXCHANGEDB_Deposit *depos = NULL; - struct TALER_Amount value; - struct GNUNET_TIME_Relative times = GNUNET_TIME_UNIT_ZERO; - unsigned long long sqrs = 0; - struct TALER_EXCHANGEDB_Refund *ref = NULL; - unsigned int *perm; - unsigned long long duration_sq; - struct GNUNET_CRYPTO_BlindingInputValues bi = { - .cipher = GNUNET_CRYPTO_BSA_RSA, - .rc = 0 - }; - struct TALER_ExchangeBlindingValues alg_values = { - .blinding_inputs = &bi - }; - struct TALER_DenomFeeSet fees; - - ref = GNUNET_new_array (ROUNDS + 1, - struct TALER_EXCHANGEDB_Refund); - depos = GNUNET_new_array (ROUNDS + 1, - struct TALER_EXCHANGEDB_Deposit); - refresh = GNUNET_new_array (ROUNDS + 1, - struct TALER_EXCHANGEDB_Refresh); - - if (NULL == - (plugin = TALER_EXCHANGEDB_plugin_load (cfg, - true))) - { - GNUNET_break (0); - result = 77; - return; - } - (void) plugin->drop_tables (plugin->cls); - if (GNUNET_OK != - plugin->create_tables (plugin->cls, - true, - num_partitions)) - { - GNUNET_break (0); - result = 77; - goto cleanup; - } - if (GNUNET_OK != - plugin->preflight (plugin->cls)) - { - GNUNET_break (0); - goto cleanup; - } - - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":1.000010", - &value)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":0.000010", - &fees.withdraw)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":0.000010", - &fees.deposit)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":0.000010", - &fees.refresh)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":0.000010", - &fees.refund)); - { - new_dkp = GNUNET_new_array (MELT_NEW_COINS, - struct DenomKeyPair *); - - for (unsigned int cnt = 0; cnt < MELT_NEW_COINS; cnt++) - { - struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); - - new_dkp[cnt] = create_denom_key_pair (RSA_KEY_SIZE, - now, - &value, - &fees); - GNUNET_assert (NULL != new_dkp[cnt]); - } - } - perm = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_NONCE, - NUM_ROWS); - FAILIF (GNUNET_OK != - plugin->start (plugin->cls, - "Transaction")); - for (unsigned int j = 0; j < NUM_ROWS; j++) - { - union GNUNET_CRYPTO_BlindingSecretP bks; - struct TALER_CoinPubHashP c_hash; - unsigned int i = perm[j]; - uint64_t known_coin_id; - struct TALER_EXCHANGEDB_CollectableBlindcoin cbc; - if (i >= ROUNDS) - i = ROUNDS; /* throw-away slot, do not keep around */ - RND_BLK (&depos[i].coin.coin_pub); - ZR_BLK (&cbc); - TALER_denom_pub_hash (&new_dkp[(unsigned int) rand () - % MELT_NEW_COINS]->pub, - &depos[i].coin.denom_pub_hash); - - - { - struct TALER_EXCHANGEDB_RefreshRevealedCoin - revealed_coins[MELT_NEW_COINS]; - - for (unsigned int p = 0; p<MELT_NEW_COINS; p++) - { - struct TALER_EXCHANGEDB_RefreshRevealedCoin *revealed_coin = - &revealed_coins[p]; - struct TALER_BlindedPlanchet *bp = &revealed_coin->blinded_planchet; - struct GNUNET_CRYPTO_RsaBlindedMessage *rp; - - bp->blinded_message = GNUNET_new (struct GNUNET_CRYPTO_BlindedMessage); - rp = &bp->blinded_message->details.rsa_blinded_message; - /* h_coin_ev must be unique, but we only have MELT_NEW_COINS created - above for NUM_ROWS iterations; instead of making "all new" coins, - we simply randomize the hash here as nobody is checking for consistency - anyway ;-) */ - bp->blinded_message->rc = 1; - bp->blinded_message->cipher = GNUNET_CRYPTO_BSA_RSA; - rp->blinded_msg_size = 1 + (size_t) GNUNET_CRYPTO_random_u64 ( - GNUNET_CRYPTO_QUALITY_WEAK, - (RSA_KEY_SIZE / 8) - 1); - rp->blinded_msg = GNUNET_malloc (rp->blinded_msg_size); - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, - rp->blinded_msg, - rp->blinded_msg_size); - TALER_denom_pub_hash (&new_dkp[(unsigned int) rand () - % MELT_NEW_COINS]->pub, - &revealed_coin->h_denom_pub); - revealed_coin->exchange_vals = alg_values; - TALER_coin_ev_hash (bp, - &revealed_coin->h_denom_pub, - &revealed_coin->coin_envelope_hash); - GNUNET_assert (GNUNET_OK == - TALER_denom_sign_blinded (&revealed_coin->coin_sig, - &new_dkp[(unsigned - int) rand () - % MELT_NEW_COINS]-> - priv, - true, - bp)); - GNUNET_assert ( - GNUNET_OK == - TALER_denom_sign_blinded ( - &cbc.sig, - &new_dkp[(unsigned int) rand () % MELT_NEW_COINS]->priv, - false, - bp)); - } - GNUNET_assert (GNUNET_OK == - TALER_denom_sig_unblind (&depos[i].coin.denom_sig, - &cbc.sig, - &bks, - &c_hash, - &alg_values, - &new_dkp[(unsigned int) rand () - % MELT_NEW_COINS]->pub)); - { - /* ENSURE_COIN_KNOWN */ - struct TALER_DenominationHashP dph; - struct TALER_AgeCommitmentHash agh; - bool zombie_required = false; - bool balance_ok; - - FAILIF (TALER_EXCHANGEDB_CKS_ADDED != - plugin->ensure_coin_known (plugin->cls, - &depos[i].coin, - &known_coin_id, - &dph, - &agh)); - /**** INSERT REFRESH COMMITMENTS ****/ - refresh[i].coin = depos[i].coin; - RND_BLK (&refresh[i].coin_sig); - RND_BLK (&refresh[i].rc); - refresh[i].amount_with_fee = value; - refresh[i].noreveal_index = MELT_NOREVEAL_INDEX; - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->do_melt (plugin->cls, - NULL, - &refresh[i], - known_coin_id, - &zombie_required, - &balance_ok)); - } - /****GET melt_serial_id generated by default****/ - { - struct TALER_EXCHANGEDB_Melt ret_refresh_session; - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->get_melt (plugin->cls, - &refresh[i].rc, - &ret_refresh_session, - &melt_serial_id)); - } - /**** INSERT REFRESH_REVEAL + TRANSFER_KEYS *****/ - { - static unsigned int cnt; - - RND_BLK (&tprivs); - RND_BLK (&tpub); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->insert_refresh_reveal (plugin->cls, - melt_serial_id, - MELT_NEW_COINS, - revealed_coins, - TALER_CNC_KAPPA - 1, - tprivs, - &tpub)); - cnt++; - // fprintf (stderr, "CNT: %u - %llu\n", cnt, (unsigned long long) melt_serial_id); - } - for (unsigned int cnt = 0; cnt < MELT_NEW_COINS; cnt++) - { - TALER_blinded_denom_sig_free (&revealed_coins[cnt].coin_sig); - TALER_blinded_planchet_free (&revealed_coins[cnt].blinded_planchet); - } - - /* { - struct TALER_CoinSpendPublicKeyP ocp; - uint64_t rrc_serial; - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->get_old_coin_by_h_blind (plugin->cls, - &revealed_coins->coin_envelope_hash, - &ocp, - &rrc_serial)); - }*/ - } - if (ROUNDS == i) - TALER_denom_sig_free (&depos[i].coin.denom_sig); - } - /* End of benchmark setup */ - GNUNET_free (perm); - FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != - plugin->commit (plugin->cls)); - for (unsigned int r = 0; r< ROUNDS; r++) - { - struct GNUNET_TIME_Absolute time; - struct GNUNET_TIME_Relative duration; - enum GNUNET_DB_QueryStatus qs; - time = GNUNET_TIME_absolute_get (); - - qs = plugin->get_link_data (plugin->cls, - &refresh[r].coin.coin_pub, - &handle_link_data_cb, - NULL); - FAILIF (qs < 0); - - duration = GNUNET_TIME_absolute_get_duration (time); - times = GNUNET_TIME_relative_add (times, - duration); - duration_sq = duration.rel_value_us * duration.rel_value_us; - GNUNET_assert (duration_sq / duration.rel_value_us == - duration.rel_value_us); - GNUNET_assert (sqrs + duration_sq >= sqrs); - sqrs += duration_sq; - } - - /* evaluation of performance */ - { - struct GNUNET_TIME_Relative avg; - double avg_dbl; - double variance; - - avg = GNUNET_TIME_relative_divide (times, - ROUNDS); - avg_dbl = avg.rel_value_us; - variance = sqrs - (avg_dbl * avg_dbl * ROUNDS); - fprintf (stdout, - "%8llu ± %6.0f\n", - (unsigned long long) avg.rel_value_us, - sqrt (variance / (ROUNDS - 1))); - } - result = 0; -drop: - // GNUNET_break (GNUNET_OK == plugin->drop_tables (plugin->cls)); -cleanup: - if (NULL != dkp) - destroy_denom_key_pair (dkp); - for (unsigned int cnt = 0; - (NULL != new_dkp) && (cnt < MELT_NEW_COINS) && (NULL != new_dkp[cnt]); - cnt++) - destroy_denom_key_pair (new_dkp[cnt]); - GNUNET_free (new_dkp); - for (unsigned int i = 0; i< ROUNDS; i++) - { - TALER_denom_sig_free (&depos[i].coin.denom_sig); - } - GNUNET_free (depos); - GNUNET_free (ref); - GNUNET_free (refresh); - dkp = NULL; - TALER_EXCHANGEDB_plugin_unload (plugin); - plugin = NULL; -} - - -int -main (int argc, - char *const argv[]) -{ - const char *plugin_name; - char *config_filename; - char *testname; - struct GNUNET_CONFIGURATION_Handle *cfg; - - (void) argc; - result = -1; - if (NULL == (plugin_name = strrchr (argv[0], (int) '-'))) - { - GNUNET_break (0); - return -1; - } - GNUNET_log_setup (argv[0], - "WARNING", - NULL); - plugin_name++; - (void) GNUNET_asprintf (&testname, - "test-exchange-db-%s", - plugin_name); - (void) GNUNET_asprintf (&config_filename, - "%s.conf", - testname); - cfg = GNUNET_CONFIGURATION_create (TALER_EXCHANGE_project_data ()); - if (GNUNET_OK != - GNUNET_CONFIGURATION_parse (cfg, - config_filename)) - { - GNUNET_break (0); - GNUNET_free (config_filename); - GNUNET_free (testname); - return 2; - } - GNUNET_SCHEDULER_run (&run, - cfg); - GNUNET_CONFIGURATION_destroy (cfg); - GNUNET_free (config_filename); - GNUNET_free (testname); - return result; -} - - -/* end of test_exchangedb_by_j.c */ diff --git a/src/exchangedb/pg_get_melt.c b/src/exchangedb/pg_get_melt.c @@ -1,124 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 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 <http://www.gnu.org/licenses/> - */ -/** - * @file exchangedb/pg_get_melt.c - * @brief Implementation of the get_melt function for Postgres - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_error_codes.h" -#include "taler_dbevents.h" -#include "taler_pq_lib.h" -#include "pg_get_melt.h" -#include "pg_helper.h" - - -enum GNUNET_DB_QueryStatus -TEH_PG_get_melt (void *cls, - const struct TALER_RefreshCommitmentP *rc, - struct TALER_EXCHANGEDB_Melt *melt, - uint64_t *melt_serial_id) -{ - struct PostgresClosure *pg = cls; - bool h_age_commitment_is_null; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (rc), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash", - &melt->session.coin. - denom_pub_hash), - TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refresh", - &melt->melt_fee), - GNUNET_PQ_result_spec_uint32 ("noreveal_index", - &melt->session.noreveal_index), - GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub", - &melt->session.coin.coin_pub), - GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig", - &melt->session.coin_sig), - GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash", - &melt->session.coin.h_age_commitment), - &h_age_commitment_is_null), - TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee", - &melt->session.amount_with_fee), - GNUNET_PQ_result_spec_uint64 ("melt_serial_id", - melt_serial_id), - GNUNET_PQ_result_spec_end - }; - enum GNUNET_DB_QueryStatus qs; - - memset (&melt->session.coin.denom_sig, - 0, - sizeof (melt->session.coin.denom_sig)); - - /* Used in #postgres_get_melt() to fetch - high-level information about a melt operation */ - PREPARE (pg, - "get_melt", - /* "SELECT" - " denoms.denom_pub_hash" - ",denoms.fee_refresh" - ",old_coin_pub" - ",old_coin_sig" - ",kc.age_commitment_hash" - ",amount_with_fee" - ",noreveal_index" - ",melt_serial_id" - " FROM refresh_commitments" - " JOIN known_coins kc" - " ON (old_coin_pub = kc.coin_pub)" - " JOIN denominations denoms" - " ON (kc.denominations_serial = denoms.denominations_serial)" - " WHERE rc=$1;", */ - "WITH rc AS MATERIALIZED ( " - " SELECT" - " * FROM refresh_commitments" - " WHERE rc=$1" - ")" - "SELECT" - " denoms.denom_pub_hash" - ",denoms.fee_refresh" - ",rc.old_coin_pub" - ",rc.old_coin_sig" - ",kc.age_commitment_hash" - ",amount_with_fee" - ",noreveal_index" - ",melt_serial_id " - "FROM (" - " SELECT" - " * " - " FROM known_coins" - " WHERE coin_pub=(SELECT old_coin_pub from rc)" - ") kc " - "JOIN rc" - " ON (kc.coin_pub=rc.old_coin_pub) " - "JOIN denominations denoms" - " USING (denominations_serial);"); - - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "get_melt", - params, - rs); - if (h_age_commitment_is_null) - memset (&melt->session.coin.h_age_commitment, - 0, - sizeof(melt->session.coin.h_age_commitment)); - - melt->session.rc = *rc; - return qs; -} diff --git a/src/exchangedb/pg_get_melt.h b/src/exchangedb/pg_get_melt.h @@ -1,44 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 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 <http://www.gnu.org/licenses/> - */ -/** - * @file exchangedb/pg_get_melt.h - * @brief implementation of the get_melt function for Postgres - * @author Christian Grothoff - */ -#ifndef PG_GET_MELT_H -#define PG_GET_MELT_H - -#include "taler_util.h" -#include "taler_json_lib.h" -#include "taler_exchangedb_plugin.h" -/** - * Lookup refresh melt commitment data under the given @a rc. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param rc commitment hash to use to locate the operation - * @param[out] melt where to store the result; note that - * melt->session.coin.denom_sig will be set to NULL - * and is not fetched by this routine (as it is not needed by the client) - * @param[out] melt_serial_id set to the row ID of @a rc in the refresh_commitments table - * @return transaction status - */ -enum GNUNET_DB_QueryStatus -TEH_PG_get_melt (void *cls, - const struct TALER_RefreshCommitmentP *rc, - struct TALER_EXCHANGEDB_Melt *melt, - uint64_t *melt_serial_id); - -#endif diff --git a/src/exchangedb/pg_insert_refresh_reveal.c b/src/exchangedb/pg_insert_refresh_reveal.c @@ -1,109 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 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 <http://www.gnu.org/licenses/> - */ -/** - * @file exchangedb/pg_insert_refresh_reveal.c - * @brief Implementation of the insert_refresh_reveal function for Postgres - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_error_codes.h" -#include "taler_dbevents.h" -#include "taler_pq_lib.h" -#include "pg_insert_refresh_reveal.h" -#include "pg_helper.h" - - -enum GNUNET_DB_QueryStatus -TEH_PG_insert_refresh_reveal ( - void *cls, - uint64_t melt_serial_id, - uint32_t num_rrcs, - const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs, - unsigned int num_tprivs, - const struct TALER_TransferPrivateKeyP *tprivs, - const struct TALER_TransferPublicKeyP *tp) -{ - struct PostgresClosure *pg = cls; - - if (TALER_CNC_KAPPA != num_tprivs + 1) - { - GNUNET_break (0); - return GNUNET_DB_STATUS_HARD_ERROR; - } - PREPARE (pg, - "insert_refresh_revealed_coin", - "INSERT INTO refresh_revealed_coins " - "(melt_serial_id " - ",freshcoin_index " - ",link_sig " - ",denominations_serial " - ",coin_ev" - ",ewv" - ",h_coin_ev" - ",ev_sig" - ") SELECT $1, $2, $3, " - " denominations_serial, $5, $6, $7, $8" - " FROM denominations" - " WHERE denom_pub_hash=$4" - " ON CONFLICT DO NOTHING;"); - for (uint32_t i = 0; i<num_rrcs; i++) - { - const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i]; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&melt_serial_id), - GNUNET_PQ_query_param_uint32 (&i), - GNUNET_PQ_query_param_auto_from_type (&rrc->orig_coin_link_sig), - GNUNET_PQ_query_param_auto_from_type (&rrc->h_denom_pub), - TALER_PQ_query_param_blinded_planchet (&rrc->blinded_planchet), - TALER_PQ_query_param_exchange_blinding_values (&rrc->exchange_vals), - GNUNET_PQ_query_param_auto_from_type (&rrc->coin_envelope_hash), - TALER_PQ_query_param_blinded_denom_sig (&rrc->coin_sig), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_refresh_revealed_coin", - params); - if (0 > qs) - return qs; - } - - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&melt_serial_id), - GNUNET_PQ_query_param_auto_from_type (tp), - GNUNET_PQ_query_param_fixed_size ( - tprivs, - num_tprivs * sizeof (struct TALER_TransferPrivateKeyP)), - GNUNET_PQ_query_param_end - }; - - /* Used in #postgres_insert_refresh_reveal() to store the transfer - keys we learned */ - PREPARE (pg, - "insert_refresh_transfer_keys", - "INSERT INTO refresh_transfer_keys " - "(melt_serial_id" - ",transfer_pub" - ",transfer_privs" - ") VALUES ($1, $2, $3)" - " ON CONFLICT DO NOTHING;"); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_refresh_transfer_keys", - params); - } -} diff --git a/src/exchangedb/pg_insert_refresh_reveal.h b/src/exchangedb/pg_insert_refresh_reveal.h @@ -1,51 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 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 <http://www.gnu.org/licenses/> - */ -/** - * @file exchangedb/pg_insert_refresh_reveal.h - * @brief implementation of the insert_refresh_reveal function for Postgres - * @author Christian Grothoff - */ -#ifndef PG_INSERT_REFRESH_REVEAL_H -#define PG_INSERT_REFRESH_REVEAL_H - -#include "taler_util.h" -#include "taler_json_lib.h" -#include "taler_exchangedb_plugin.h" -/** - * Store in the database which coin(s) the wallet wanted to create - * in a given refresh operation and all of the other information - * we learned or created in the /refresh/reveal step. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param melt_serial_id row ID of the commitment / melt operation in refresh_commitments - * @param num_rrcs number of coins to generate, size of the @a rrcs array - * @param rrcs information about the new coins - * @param num_tprivs number of entries in @a tprivs, should be #TALER_CNC_KAPPA - 1 - * @param tprivs transfer private keys to store - * @param tp public key to store - * @return query status for the transaction - */ -enum GNUNET_DB_QueryStatus -TEH_PG_insert_refresh_reveal ( - void *cls, - uint64_t melt_serial_id, - uint32_t num_rrcs, - const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs, - unsigned int num_tprivs, - const struct TALER_TransferPrivateKeyP *tprivs, - const struct TALER_TransferPublicKeyP *tp); - -#endif diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c @@ -83,7 +83,6 @@ #include "pg_get_global_fees.h" #include "pg_get_known_coin.h" #include "pg_get_kyc_rules.h" -#include "pg_get_melt.h" #include "pg_get_old_coin_by_h_blind.h" #include "pg_get_pending_kyc_requirement_process.h" #include "pg_get_policy_details.h" @@ -122,7 +121,6 @@ #include "pg_insert_partner.h" #include "pg_insert_purse_request.h" #include "pg_insert_records_by_table.h" -#include "pg_insert_refresh_reveal.h" #include "pg_insert_refund.h" #include "pg_insert_reserve_closed.h" #include "pg_insert_reserve_open_deposit.h" @@ -566,8 +564,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) = &TEH_PG_add_policy_fulfillment_proof; plugin->do_refresh = &TEH_PG_do_refresh; - plugin->do_melt - = &TEH_PG_do_melt; plugin->do_refund = &TEH_PG_do_refund; plugin->do_recoup @@ -606,14 +602,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) = &TEH_PG_select_refunds_by_coin; plugin->select_aml_measures = &TEH_PG_select_aml_measures; - plugin->get_melt - = &TEH_PG_get_melt; - plugin->insert_refresh_reveal - = &TEH_PG_insert_refresh_reveal; plugin->get_refresh = &TEH_PG_get_refresh; - plugin->get_refresh_reveal - = &TEH_PG_get_refresh_reveal; plugin->lookup_wire_transfer = &TEH_PG_lookup_wire_transfer; plugin->lookup_transfer_by_deposit diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h @@ -1598,79 +1598,13 @@ void TALER_EXCHANGE_refund_cancel (struct TALER_EXCHANGE_RefundHandle *refund); -/* ********************* POST /csr-melt *********************** */ - - -/** - * @brief A /csr-melt Handle - */ -struct TALER_EXCHANGE_CsRMeltHandle; - - /** - * Details about a response for a CS R request. - */ -struct TALER_EXCHANGE_CsRMeltResponse -{ - /** - * HTTP response data. - */ - struct TALER_EXCHANGE_HttpResponse hr; - - /** - * Details about the response. - */ - union - { - /** - * Details if the status is #MHD_HTTP_OK. - */ - struct - { - /** - * Length of the @e blinding_values array. - */ - unsigned int blinding_values_len; - - /** - * Values contributed by the exchange for the - * respective coin's withdraw operation. - */ - const struct TALER_ExchangeBlindingValues *blinding_values; - } ok; - - /** - * Details if the status is #MHD_HTTP_GONE. - */ - struct - { - /* FIXME: returning full details is not implemented */ - } gone; - - } details; -}; - - -/** - * Callbacks of this type are used to serve the result of submitting a - * CS R request to a exchange. - * - * @param cls closure - * @param csrr response details - */ -typedef void -(*TALER_EXCHANGE_CsRMeltCallback) ( - void *cls, - const struct TALER_EXCHANGE_CsRMeltResponse *csrr); - - -/** - * Information we pass per coin to a /csr-melt request. + * Information we pass per coin to a /blinding-prepare request. */ struct TALER_EXCHANGE_NonceKey { /** - * Which denomination key is the /csr-melt request for? + * Which denomination key is the /blinding-prepare request for? */ const struct TALER_EXCHANGE_DenomPublicKey *pk; @@ -1682,42 +1616,6 @@ struct TALER_EXCHANGE_NonceKey }; -/** - * Get a set of CS R values using a /csr-melt request. - * - * @param ctx curl context - * @param url exchange base URL - * @param rms master key used for the derivation of the CS values - * @param nks_len length of the @a nks array - * @param nks array of denominations and nonces - * @param res_cb the callback to call when the final result for this request is available - * @param res_cb_cls closure for the above callback - * @return handle for the operation on success, NULL on error, i.e. - * if the inputs are invalid (i.e. denomination key not with this exchange). - * In this case, the callback is not called. - */ -struct TALER_EXCHANGE_CsRMeltHandle * -TALER_EXCHANGE_csr_melt ( - struct GNUNET_CURL_Context *ctx, - const char *url, - const struct TALER_RefreshMasterSecretP *rms, - unsigned int nks_len, - struct TALER_EXCHANGE_NonceKey nks[static nks_len], - TALER_EXCHANGE_CsRMeltCallback res_cb, - void *res_cb_cls); - - -/** - * - * Cancel a CS R melt request. This function cannot be used - * on a request handle if a response is already served for it. - * - * @param csrh the withdraw handle - */ -void -TALER_EXCHANGE_csr_melt_cancel (struct TALER_EXCHANGE_CsRMeltHandle *csrh); - - /* ********************* GET /coins/$COIN_PUB *********************** */ /** @@ -3426,6 +3324,41 @@ TALER_EXCHANGE_reveal_withdraw_cancel ( /* ********************* /reveal-melt ************************ */ + +/** + * Information about a coin obtained via /reveal-melt. + */ +struct TALER_EXCHANGE_RevealedCoinInfo +{ + /** + * Private key of the coin. + */ + struct TALER_CoinSpendPrivateKeyP coin_priv; + + /** + * Master secret of this coin. + */ + struct TALER_PlanchetMasterSecretP ps; + + /** + * Age commitment and its hash of the coin, might be NULL. + */ + struct TALER_AgeCommitmentProof *age_commitment_proof; + struct TALER_AgeCommitmentHash h_age_commitment; + + /** + * Blinding keys used to blind the fresh coin. + */ + union GNUNET_CRYPTO_BlindingSecretP bks; + + /** + * Signature affirming the validity of the coin. + */ + struct TALER_DenominationSignature sig; + +}; + + /** * @brief A handle to a /reveal-melt request */ @@ -3624,187 +3557,6 @@ TALER_EXCHANGE_reveal_melt_cancel ( struct TALER_EXCHANGE_RevealMeltHandle *awrh); -/* ********************* /refresh/melt+reveal ***************************** */ - - -/** - * Information needed to melt (partially spent) coins to obtain fresh coins - * that are unlinkable to the original coin(s). Note that melting more than - * one coin in a single request will make those coins linkable, so we only melt - * one coin at a time. - */ -struct TALER_EXCHANGE_RefreshData -{ - /** - * private key of the coin to melt - */ - struct TALER_CoinSpendPrivateKeyP melt_priv; - - /** - * age commitment and proof that went into the original coin, - * might be NULL. - */ - const struct TALER_AgeCommitmentProof *melt_age_commitment_proof; - - /** - * Hash of age commitment and proof that went into the original coin, - * might be NULL. - */ - const struct TALER_AgeCommitmentHash *melt_h_age_commitment; - - /** - * amount specifying how much the coin will contribute to the melt - * (including fee) - */ - struct TALER_Amount melt_amount; - - /** - * signatures affirming the validity of the public keys corresponding to the - * @e melt_priv private key - */ - struct TALER_DenominationSignature melt_sig; - - /** - * denomination key information record corresponding to the @e melt_sig - * validity of the keys - */ - struct TALER_EXCHANGE_DenomPublicKey melt_pk; - - /** - * array of @e pks_len denominations of fresh coins to create - */ - const struct TALER_EXCHANGE_DenomPublicKey *fresh_pks; - - /** - * length of the @e pks array - */ - unsigned int fresh_pks_len; -}; - - -/* ********************* /coins/$COIN_PUB/melt ***************************** */ - -/** - * @brief A /coins/$COIN_PUB/melt Handle - */ -struct TALER_EXCHANGE_MeltHandle; - - -/** - * Information we obtain per coin during melting. - */ -struct TALER_EXCHANGE_MeltBlindingDetail -{ - /** - * Exchange values contributed to the refresh operation - */ - struct TALER_ExchangeBlindingValues alg_value; - -}; - - -/** - * Response returned to a /melt request. - */ -struct TALER_EXCHANGE_MeltResponse -{ - /** - * Full HTTP response details. - */ - struct TALER_EXCHANGE_HttpResponse hr; - - /** - * Parsed response details, variant depending on the - * @e hr.http_status. - */ - union - { - /** - * Results for status #MHD_HTTP_OK. - */ - struct - { - - /** - * Information returned per coin. - */ - const struct TALER_EXCHANGE_MeltBlindingDetail *mbds; - - /** - * Key used by the exchange to sign the response. - */ - struct TALER_ExchangePublicKeyP sign_key; - - /** - * Length of the @a mbds array with the exchange values - * and blinding keys we are using. - */ - unsigned int num_mbds; - - /** - * Gamma value chosen by the exchange. - */ - uint32_t noreveal_index; - } ok; - - } details; -}; - - -/** - * Callbacks of this type are used to notify the application about the result - * of the /coins/$COIN_PUB/melt stage. If successful, the @a noreveal_index - * should be committed to disk prior to proceeding - * #TALER_EXCHANGE_refreshes_reveal(). - * - * @param cls closure - * @param mr response details - */ -typedef void -(*TALER_EXCHANGE_MeltCallback) ( - void *cls, - const struct TALER_EXCHANGE_MeltResponse *mr); - - -/** - * Submit a melt request to the exchange and get the exchange's - * response. - * - * This API is typically used by a wallet. Note that to ensure that - * no money is lost in case of hardware failures, the provided - * argument @a rd should be committed to persistent storage - * prior to calling this function. - * - * @param ctx curl context - * @param url exchange base URL - * @param keys exchange keys - * @param rms the fresh secret that defines the refresh operation - * @param rd the refresh data specifying the characteristics of the operation - * @param melt_cb the callback to call with the result - * @param melt_cb_cls closure for @a melt_cb - * @return a handle for this request; NULL if the argument was invalid. - * In this case, neither callback will be called. - */ -struct TALER_EXCHANGE_MeltHandle * -TALER_EXCHANGE_melt ( - struct GNUNET_CURL_Context *ctx, - const char *url, - struct TALER_EXCHANGE_Keys *keys, - const struct TALER_RefreshMasterSecretP *rms, - const struct TALER_EXCHANGE_RefreshData *rd, - TALER_EXCHANGE_MeltCallback melt_cb, - void *melt_cb_cls); - - -/** - * Cancel a melt request. This function cannot be used - * on a request handle if the callback was already invoked. - * - * @param mh the melt handle - */ -void -TALER_EXCHANGE_melt_cancel (struct TALER_EXCHANGE_MeltHandle *mh); - /* ********************* /melt (starting with v27) **************************** */ /** @@ -3919,151 +3671,6 @@ void TALER_EXCHANGE_melt_v27_cancel (struct TALER_EXCHANGE_MeltHandle_v27 *mh); -/* ********************* /refreshes/$RCH/reveal ***************************** */ - - -/** - * Information about a coin obtained via /refreshes/$RCH/reveal. - */ -struct TALER_EXCHANGE_RevealedCoinInfo -{ - /** - * Private key of the coin. - */ - struct TALER_CoinSpendPrivateKeyP coin_priv; - - /** - * Master secret of this coin. - */ - struct TALER_PlanchetMasterSecretP ps; - - /** - * Age commitment and its hash of the coin, might be NULL. - */ - struct TALER_AgeCommitmentProof *age_commitment_proof; - struct TALER_AgeCommitmentHash h_age_commitment; - - /** - * Blinding keys used to blind the fresh coin. - */ - union GNUNET_CRYPTO_BlindingSecretP bks; - - /** - * Signature affirming the validity of the coin. - */ - struct TALER_DenominationSignature sig; - -}; - - -/** - * Result of a /refreshes/$RCH/reveal request. - */ -struct TALER_EXCHANGE_RevealResult -{ - /** - * HTTP status. - */ - struct TALER_EXCHANGE_HttpResponse hr; - - /** - * Parsed response details, variant depending on the - * @e hr.http_status. - */ - union - { - /** - * Results for status #MHD_HTTP_OK. - */ - struct - { - /** - * Array of @e num_coins values about the coins obtained via the refresh - * operation. The array give the coins in the same order (and should - * have the same length) in which the original melt request specified the - * respective denomination keys. - */ - const struct TALER_EXCHANGE_RevealedCoinInfo *coins; - - /** - * Number of coins returned. - */ - unsigned int num_coins; - } ok; - - } details; - -}; - - -/** - * Callbacks of this type are used to return the final result of - * submitting a refresh request to a exchange. If the operation was - * successful, this function returns the signatures over the coins - * that were remelted. - * - * @param cls closure - * @param rr result of the reveal operation - */ -typedef void -(*TALER_EXCHANGE_RefreshesRevealCallback)( - void *cls, - const struct TALER_EXCHANGE_RevealResult *rr); - - -/** - * @brief A /refreshes/$RCH/reveal Handle - */ -struct TALER_EXCHANGE_RefreshesRevealHandle; - - -/** - * Submit a /refreshes/$RCH/reval request to the exchange and get the exchange's - * response. - * - * This API is typically used by a wallet. Note that to ensure that - * no money is lost in case of hardware failures, the provided - * arguments should have been committed to persistent storage - * prior to calling this function. - * - * @param ctx curl context - * @param url exchange base URL - * @param rms the fresh secret that defines the refresh operation - * @param rd the refresh data that characterizes the refresh operation - * @param num_coins number of fresh coins to be created, length of the @a exchange_vals array, must match value in @a rd - * @param blinding_values array @a num_coins of exchange values contributed to the refresh operation - * @param noreveal_index response from the exchange to the - * #TALER_EXCHANGE_melt() invocation - * @param reveal_cb the callback to call with the final result of the - * refresh operation - * @param reveal_cb_cls closure for the above callback - * @return a handle for this request; NULL if the argument was invalid. - * In this case, neither callback will be called. - */ -struct TALER_EXCHANGE_RefreshesRevealHandle * -TALER_EXCHANGE_refreshes_reveal ( - struct GNUNET_CURL_Context *ctx, - const char *url, - const struct TALER_RefreshMasterSecretP *rms, - const struct TALER_EXCHANGE_RefreshData *rd, - unsigned int num_coins, - const struct TALER_ExchangeBlindingValues blinding_values[static num_coins], - uint32_t noreveal_index, - TALER_EXCHANGE_RefreshesRevealCallback reveal_cb, - void *reveal_cb_cls); - - -/** - * Cancel a refresh reveal request. This function cannot be used - * on a request handle if the callback was already invoked. - * - * @param rrh the refresh reval handle - */ -void -TALER_EXCHANGE_refreshes_reveal_cancel ( - struct TALER_EXCHANGE_RefreshesRevealHandle *rrh); - - /* ********************* /transfers/$WTID *********************** */ /** diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h @@ -2650,7 +2650,7 @@ struct TALER_EXCHANGEDB_Refresh_v27 /** * [out]-Array of @a num_coins hashes of the public keys of the denominations * identified by @e denom_serials. This field is set when calling - * get_melt_v27 + * get_refresh */ struct TALER_DenominationHashP *denom_pub_hashes; }; @@ -3151,7 +3151,7 @@ struct TALER_EXCHANGEDB_RefreshRevealedCoin /** * Values contributed from the exchange to the - * coin generation (see /csr). + * coin generation (see /blinding-prepare). */ struct TALER_ExchangeBlindingValues exchange_vals; @@ -4567,30 +4567,6 @@ struct TALER_EXCHANGEDB_Plugin struct GNUNET_TIME_Timestamp *exchange_timestamp, bool *is_idempotent); - - /** - * Perform melt operation, checking for sufficient balance - * of the coin and possibly persisting the melt details. - * - * @param cls the plugin-specific state - * @param rms client-contributed input for CS denominations that must be checked for idempotency, or NULL for non-CS withdrawals - * @param[in,out] refresh refresh operation details; the noreveal_index - * is set in case the coin was already melted before - * @param known_coin_id row of the coin in the known_coins table - * @param[in,out] zombie_required true if the melt must only succeed if the coin is a zombie, set to false if the requirement was satisfied - * @param[out] balance_ok set to true if the balance was sufficient - * @return query execution status - */ - enum GNUNET_DB_QueryStatus - (*do_melt)( - void *cls, - const struct TALER_RefreshMasterSecretP *rms, - struct TALER_EXCHANGEDB_Refresh *refresh, - uint64_t known_coin_id, - bool *zombie_required, - bool *balance_ok); - - /** * Perform refresh operation--introduced with v27 of the API--, * checking for sufficient balance of the coin and possibly persisting the melt/refresh details. @@ -4652,28 +4628,6 @@ struct TALER_EXCHANGEDB_Plugin /** - * Check if the given @a nonce was properly locked to the given @a old_coin_pub. If so, check if we already - * created CS signatures for the given @a nonce and @a new_denom_pub_hashes, - * and if so, return them in @a s_scalars. Otherwise, persist the - * signatures from @a s_scalars in the database. - * - * @param cls the plugin-specific state - * @param nonce the client-provided nonce where we must prevent reuse - * @param old_coin_pub public key the nonce was locked to - * @param num_fresh_coins array length, number of fresh coins revealed - * @param[in,out] crfcds array of data about the fresh coins, of length @a num_fresh_coins - * @return query execution status - */ - enum GNUNET_DB_QueryStatus - (*cs_refreshes_reveal)( - void *cls, - const struct GNUNET_CRYPTO_CsSessionNonce *nonce, - const struct TALER_CoinSpendPublicKeyP *old_coin_pub, - unsigned int num_fresh_coins, - struct TALER_EXCHANGEDB_CsRevealFreshCoinData *crfcds); - - - /** * Perform refund operation, checking for sufficient deposits * of the coin and possibly persisting the refund details. * @@ -5175,66 +5129,6 @@ struct TALER_EXCHANGEDB_Plugin const struct TALER_FullPaytoHashP *h_payto, const struct TALER_WireTransferIdentifierRawP *wtid); - - /** - * Lookup melt commitment data under the given @a rc. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param rc commitment to use for the lookup - * @param[out] melt where to store the result; note that - * melt->session.coin.denom_sig will be set to NULL - * and is not fetched by this routine (as it is not needed by the client) - * @param[out] melt_serial_id set to the row ID of @a rc in the refresh_commitments table - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*get_melt)(void *cls, - const struct TALER_RefreshCommitmentP *rc, - struct TALER_EXCHANGEDB_Melt *melt, - uint64_t *melt_serial_id); - - - /** - * Store in the database which coin(s) the wallet wanted to create - * in a given refresh operation and all of the other information - * we learned or created in the reveal step. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param melt_serial_id row ID of the commitment / melt operation in refresh_commitments - * @param num_rrcs number of coins to generate, size of the @a rrcs array - * @param rrcs information about the new coins - * @param num_tprivs number of entries in @a tprivs, should be #TALER_CNC_KAPPA - 1 - * @param tprivs transfer private keys to store - * @param tp public key to store - * @return query status for the transaction - */ - enum GNUNET_DB_QueryStatus - (*insert_refresh_reveal)( - void *cls, - uint64_t melt_serial_id, - uint32_t num_rrcs, - const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs, - unsigned int num_tprivs, - const struct TALER_TransferPrivateKeyP *tprivs, - const struct TALER_TransferPublicKeyP *tp); - - - /** - * Lookup in the database for the fresh coins that we - * created in the given refresh operation. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param rc identify commitment and thus refresh operation - * @param cb function to call with the results - * @param cb_cls closure for @a cb - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*get_refresh_reveal)(void *cls, - const struct TALER_RefreshCommitmentP *rc, - TALER_EXCHANGEDB_RefreshCallback cb, - void *cb_cls); - /** * Compile a list of (historic) transactions performed with the given coin * (melt, refund, recoup and deposit operations). Should return 0 if the @a diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am @@ -28,7 +28,6 @@ libtalerexchange_la_SOURCES = \ exchange_api_coins_history.c \ exchange_api_common.c exchange_api_common.h \ exchange_api_contracts_get.c \ - exchange_api_csr_melt.c \ exchange_api_curl_defaults.c exchange_api_curl_defaults.h \ exchange_api_deposits_get.c \ exchange_api_get_aml_measures.c \ @@ -55,7 +54,6 @@ libtalerexchange_la_SOURCES = \ exchange_api_management_update_aml_officer.c \ exchange_api_management_wire_disable.c \ exchange_api_management_wire_enable.c \ - exchange_api_melt.c \ exchange_api_melt_v27.c \ exchange_api_purse_create_with_deposit.c \ exchange_api_purse_create_with_merge.c \ @@ -66,7 +64,6 @@ libtalerexchange_la_SOURCES = \ exchange_api_recoup.c \ exchange_api_recoup_refresh.c \ exchange_api_refresh_common.c exchange_api_refresh_common.h \ - exchange_api_refreshes_reveal.c \ exchange_api_refund.c \ exchange_api_reserves_attest.c \ exchange_api_reserves_close.c \ diff --git a/src/lib/exchange_api_csr_melt.c b/src/lib/exchange_api_csr_melt.c @@ -1,320 +0,0 @@ -/* - 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 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 - <http://www.gnu.org/licenses/> -*/ -/** - * @file lib/exchange_api_csr_melt.c - * @brief Implementation of /csr-melt requests (get R in exchange used for Clause Schnorr refresh) - * @author Lucien Heuzeveldt - * @author Gian Demarmels - */ -#include "platform.h" -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_json_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler_exchange_service.h" -#include "taler_json_lib.h" -#include "exchange_api_handle.h" -#include "taler_signatures.h" -#include "exchange_api_curl_defaults.h" - - -/** - * @brief A Clause Schnorr R Handle - */ -struct TALER_EXCHANGE_CsRMeltHandle -{ - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_CsRMeltCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Context for #TEH_curl_easy_post(). Keeps the data that must - * persist for Curl to make the upload. - */ - struct TALER_CURL_PostContext post_ctx; -}; - - -/** - * We got a 200 OK response for the /reserves/$RESERVE_PUB/withdraw operation. - * Extract the coin's signature and return it to the caller. The signature we - * get from the exchange is for the blinded value. Thus, we first must - * unblind it and then should verify its validity against our coin's hash. - * - * If everything checks out, we return the unblinded signature - * to the application via the callback. - * - * @param csrh operation handle - * @param arr reply from the exchange - * @param hr http response details - * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors - */ -static enum GNUNET_GenericReturnValue -csr_ok (struct TALER_EXCHANGE_CsRMeltHandle *csrh, - const json_t *arr, - struct TALER_EXCHANGE_HttpResponse *hr) -{ - size_t alen = json_array_size (arr); - struct TALER_ExchangeBlindingValues blinding_values[GNUNET_NZL (alen)]; - struct TALER_EXCHANGE_CsRMeltResponse csrr = { - .hr = *hr, - .details.ok.blinding_values_len = alen, - .details.ok.blinding_values = blinding_values - }; - - for (size_t i = 0; i<alen; i++) - { - json_t *av = json_array_get (arr, - i); - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_exchange_blinding_values ( - "ewv", - &blinding_values[i]), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (av, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - csrh->cb (csrh->cb_cls, - &csrr); - for (size_t i = 0; i<alen; i++) - TALER_denom_ewv_free (&blinding_values[i]); - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the HTTP /csr request. - * - * @param cls the `struct TALER_EXCHANGE_CsRMeltHandle` - * @param response_code HTTP response code, 0 on error - * @param response parsed JSON result, NULL on error - */ -static void -handle_csr_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_CsRMeltHandle *csrh = cls; - const json_t *j = response; - struct TALER_EXCHANGE_HttpResponse hr = { - .reply = j, - .http_status = (unsigned int) response_code - }; - struct TALER_EXCHANGE_CsRMeltResponse csrr = { - .hr = hr - }; - - csrh->job = NULL; - switch (response_code) - { - case 0: - csrr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - { - json_t *arr; - - arr = json_object_get (j, - "ewvs"); - if ( (NULL == arr) || - (0 == json_array_size (arr)) || - (GNUNET_OK != - csr_ok (csrh, - arr, - &hr)) ) - { - GNUNET_break_op (0); - csrr.hr.http_status = 0; - csrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - break; - } - } - TALER_EXCHANGE_csr_melt_cancel (csrh); - return; - case MHD_HTTP_BAD_REQUEST: - /* This should never happen, either us or the exchange is buggy - (or API version conflict); just pass JSON reply to the application */ - csrr.hr.ec = TALER_JSON_get_error_code (j); - csrr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_NOT_FOUND: - /* Nothing really to verify, the exchange basically just says - that it doesn't know the /csr endpoint or denomination. - Can happen if the exchange doesn't support Clause Schnorr. - We should simply pass the JSON reply to the application. */ - csrr.hr.ec = TALER_JSON_get_error_code (j); - csrr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_GONE: - /* could happen if denomination was revoked */ - /* Note: one might want to check /keys for revocation - signature here, alas tricky in case our /keys - is outdated => left to clients */ - csrr.hr.ec = TALER_JSON_get_error_code (j); - csrr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - csrr.hr.ec = TALER_JSON_get_error_code (j); - csrr.hr.hint = TALER_JSON_get_error_hint (j); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - csrr.hr.ec = TALER_JSON_get_error_code (j); - csrr.hr.hint = TALER_JSON_get_error_hint (j); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for CS R request\n", - (unsigned int) response_code, - (int) hr.ec); - break; - } - csrh->cb (csrh->cb_cls, - &csrr); - csrh->cb = NULL; - TALER_EXCHANGE_csr_melt_cancel (csrh); -} - - -struct TALER_EXCHANGE_CsRMeltHandle * -TALER_EXCHANGE_csr_melt ( - struct GNUNET_CURL_Context *ctx, - const char *url, - const struct TALER_RefreshMasterSecretP *rms, - unsigned int nks_len, - struct TALER_EXCHANGE_NonceKey nks[static nks_len], - TALER_EXCHANGE_CsRMeltCallback res_cb, - void *res_cb_cls) -{ - struct TALER_EXCHANGE_CsRMeltHandle *csrh; - json_t *csr_arr; - - if (0 == nks_len) - { - GNUNET_break (0); - return NULL; - } - for (unsigned int i = 0; i<nks_len; i++) - if (GNUNET_CRYPTO_BSA_CS != - nks[i].pk->key.bsign_pub_key->cipher) - { - GNUNET_break (0); - return NULL; - } - csrh = GNUNET_new (struct TALER_EXCHANGE_CsRMeltHandle); - csrh->cb = res_cb; - csrh->cb_cls = res_cb_cls; - csr_arr = json_array (); - GNUNET_assert (NULL != csr_arr); - for (unsigned int i = 0; i<nks_len; i++) - { - const struct TALER_EXCHANGE_NonceKey *nk = &nks[i]; - json_t *csr_obj; - - csr_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_uint64 ("coin_offset", - nk->cnc_num), - GNUNET_JSON_pack_data_auto ("denom_pub_hash", - &nk->pk->h_key)); - GNUNET_assert (NULL != csr_obj); - GNUNET_assert (0 == - json_array_append_new (csr_arr, - csr_obj)); - } - csrh->url = TALER_url_join (url, - "csr-melt", - NULL); - if (NULL == csrh->url) - { - json_decref (csr_arr); - GNUNET_free (csrh); - return NULL; - } - { - CURL *eh; - json_t *req; - - req = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("rms", - rms), - GNUNET_JSON_pack_array_steal ("nks", - csr_arr)); - eh = TALER_EXCHANGE_curl_easy_get_ (csrh->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&csrh->post_ctx, - eh, - req)) ) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (req); - GNUNET_free (csrh->url); - GNUNET_free (csrh); - return NULL; - } - json_decref (req); - csrh->job = GNUNET_CURL_job_add2 (ctx, - eh, - csrh->post_ctx.headers, - &handle_csr_finished, - csrh); - } - return csrh; -} - - -void -TALER_EXCHANGE_csr_melt_cancel (struct TALER_EXCHANGE_CsRMeltHandle *csrh) -{ - if (NULL != csrh->job) - { - GNUNET_CURL_job_cancel (csrh->job); - csrh->job = NULL; - } - GNUNET_free (csrh->url); - TALER_curl_easy_post_finished (&csrh->post_ctx); - GNUNET_free (csrh); -} diff --git a/src/lib/exchange_api_melt.c b/src/lib/exchange_api_melt.c @@ -1,602 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2015-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 - <http://www.gnu.org/licenses/> -*/ -/** - * @file lib/exchange_api_melt.c - * @brief Implementation of the /coins/$COIN_PUB/melt request - * @author Christian Grothoff - */ -#include "platform.h" -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_json_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler_json_lib.h" -#include "taler_exchange_service.h" -#include "exchange_api_common.h" -#include "exchange_api_handle.h" -#include "taler_signatures.h" -#include "exchange_api_curl_defaults.h" -#include "exchange_api_refresh_common.h" - - -/** - * @brief A /coins/$COIN_PUB/melt Handle - */ -struct TALER_EXCHANGE_MeltHandle -{ - - /** - * The keys of the this request handle will use - */ - struct TALER_EXCHANGE_Keys *keys; - - /** - * The url for this request. - */ - char *url; - - /** - * The exchange base url. - */ - char *exchange_url; - - /** - * Curl context. - */ - struct GNUNET_CURL_Context *cctx; - - /** - * Context for #TEH_curl_easy_post(). Keeps the data that must - * persist for Curl to make the upload. - */ - struct TALER_CURL_PostContext ctx; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with refresh melt failure results. - */ - TALER_EXCHANGE_MeltCallback melt_cb; - - /** - * Closure for @e result_cb and @e melt_failure_cb. - */ - void *melt_cb_cls; - - /** - * Actual information about the melt operation. - */ - struct MeltData md; - - /** - * The secret the entire melt operation is seeded from. - */ - struct TALER_RefreshMasterSecretP rms; - - /** - * Details about the characteristics of the requested melt operation. - */ - const struct TALER_EXCHANGE_RefreshData *rd; - - /** - * Array of `num_fresh_coins` per-coin values - * returned from melt operation. - */ - struct TALER_EXCHANGE_MeltBlindingDetail *mbds; - - /** - * Handle for the preflight request, or NULL. - */ - struct TALER_EXCHANGE_CsRMeltHandle *csr; - - /** - * Public key of the coin being melted. - */ - struct TALER_CoinSpendPublicKeyP coin_pub; - - /** - * Signature affirming the melt. - */ - struct TALER_CoinSpendSignatureP coin_sig; - - /** - * @brief Public information about the coin's denomination key - */ - const struct TALER_EXCHANGE_DenomPublicKey *dki; - - /** - * Gamma value chosen by the exchange during melt. - */ - uint32_t noreveal_index; - - /** - * True if we need to include @e rms in our melt request. - */ - bool send_rms; -}; - - -/** - * Verify that the signature on the "200 OK" response - * from the exchange is valid. - * - * @param[in,out] mh melt handle - * @param json json reply with the signature - * @param[out] exchange_pub public key of the exchange used for the signature - * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not - */ -static enum GNUNET_GenericReturnValue -verify_melt_signature_ok (struct TALER_EXCHANGE_MeltHandle *mh, - const json_t *json, - struct TALER_ExchangePublicKeyP *exchange_pub) -{ - struct TALER_ExchangeSignatureP exchange_sig; - const struct TALER_EXCHANGE_Keys *key_state; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("exchange_sig", - &exchange_sig), - GNUNET_JSON_spec_fixed_auto ("exchange_pub", - exchange_pub), - GNUNET_JSON_spec_uint32 ("noreveal_index", - &mh->noreveal_index), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - /* check that exchange signing key is permitted */ - key_state = mh->keys; - if (GNUNET_OK != - TALER_EXCHANGE_test_signing_key (key_state, - exchange_pub)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - /* check that noreveal index is in permitted range */ - if (TALER_CNC_KAPPA <= mh->noreveal_index) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - if (GNUNET_OK != - TALER_exchange_online_melt_confirmation_verify ( - &mh->md.rc, - mh->noreveal_index, - exchange_pub, - &exchange_sig)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP /coins/$COIN_PUB/melt request. - * - * @param cls the `struct TALER_EXCHANGE_MeltHandle` - * @param response_code HTTP response code, 0 on error - * @param response parsed JSON result, NULL on error - */ -static void -handle_melt_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_MeltHandle *mh = cls; - const json_t *j = response; - struct TALER_EXCHANGE_MeltResponse mr = { - .hr.reply = j, - .hr.http_status = (unsigned int) response_code - }; - - mh->job = NULL; - switch (response_code) - { - case 0: - mr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - if (GNUNET_OK != - verify_melt_signature_ok (mh, - j, - &mr.details.ok.sign_key)) - { - GNUNET_break_op (0); - mr.hr.http_status = 0; - mr.hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE; - break; - } - mr.details.ok.noreveal_index = mh->noreveal_index; - mr.details.ok.num_mbds = mh->rd->fresh_pks_len; - mr.details.ok.mbds = mh->mbds; - mh->melt_cb (mh->melt_cb_cls, - &mr); - mh->melt_cb = NULL; - break; - case MHD_HTTP_BAD_REQUEST: - /* This should never happen, either us or the exchange is buggy - (or API version conflict); just pass JSON reply to the application */ - mr.hr.ec = TALER_JSON_get_error_code (j); - mr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_CONFLICT: - mr.hr.ec = TALER_JSON_get_error_code (j); - mr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_FORBIDDEN: - /* Nothing really to verify, exchange says one of the signatures is - invalid; assuming we checked them, this should never happen, we - should pass the JSON reply to the application */ - mr.hr.ec = TALER_JSON_get_error_code (j); - mr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_NOT_FOUND: - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the application */ - mr.hr.ec = TALER_JSON_get_error_code (j); - mr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - mr.hr.ec = TALER_JSON_get_error_code (j); - mr.hr.hint = TALER_JSON_get_error_hint (j); - break; - default: - /* unexpected response code */ - mr.hr.ec = TALER_JSON_get_error_code (j); - mr.hr.hint = TALER_JSON_get_error_hint (j); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange melt\n", - (unsigned int) response_code, - mr.hr.ec); - GNUNET_break_op (0); - break; - } - if (NULL != mh->melt_cb) - mh->melt_cb (mh->melt_cb_cls, - &mr); - TALER_EXCHANGE_melt_cancel (mh); -} - - -/** - * Start the actual melt operation, now that we have - * the exchange's input values. - * - * @param[in,out] mh melt operation to run - * @return #GNUNET_OK if we could start the operation - */ -static enum GNUNET_GenericReturnValue -start_melt (struct TALER_EXCHANGE_MeltHandle *mh) -{ - const struct TALER_EXCHANGE_Keys *key_state; - json_t *melt_obj; - CURL *eh; - char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32]; - struct TALER_DenominationHashP h_denom_pub; - struct TALER_ExchangeBlindingValues blinding_values[mh->rd->fresh_pks_len]; - - for (unsigned int i = 0; i<mh->rd->fresh_pks_len; i++) - { - if (GNUNET_CRYPTO_BSA_RSA == - mh->rd->fresh_pks[i].key.bsign_pub_key->cipher) - blinding_values[i] = *TALER_denom_ewv_rsa_singleton (); - else - blinding_values[i] = mh->mbds[i].alg_value; - } - if (GNUNET_OK != - TALER_EXCHANGE_get_melt_data_ (&mh->rms, - mh->rd, - blinding_values, - &mh->md)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - TALER_denom_pub_hash (&mh->md.melted_coin.pub_key, - &h_denom_pub); - TALER_wallet_melt_sign ( - &mh->md.melted_coin.melt_amount_with_fee, - &mh->md.melted_coin.fee_melt, - &mh->md.rc, - &h_denom_pub, - mh->md.melted_coin.h_age_commitment, - &mh->md.melted_coin.coin_priv, - &mh->coin_sig); - GNUNET_CRYPTO_eddsa_key_get_public (&mh->md.melted_coin.coin_priv.eddsa_priv, - &mh->coin_pub.eddsa_pub); - melt_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("denom_pub_hash", - &h_denom_pub), - TALER_JSON_pack_denom_sig ("denom_sig", - &mh->md.melted_coin.sig), - GNUNET_JSON_pack_data_auto ("confirm_sig", - &mh->coin_sig), - TALER_JSON_pack_amount ("value_with_fee", - &mh->md.melted_coin.melt_amount_with_fee), - GNUNET_JSON_pack_data_auto ("rc", - &mh->md.rc), - GNUNET_JSON_pack_allow_null ( - (NULL != mh->md.melted_coin.h_age_commitment) - ? GNUNET_JSON_pack_data_auto ("age_commitment_hash", - mh->md.melted_coin.h_age_commitment) - : GNUNET_JSON_pack_string ("age_commitment_hash", - NULL)), - GNUNET_JSON_pack_allow_null ( - mh->send_rms - ? GNUNET_JSON_pack_data_auto ("rms", - &mh->rms) - : GNUNET_JSON_pack_string ("rms", - NULL))); - { - char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2]; - char *end; - - end = GNUNET_STRINGS_data_to_string ( - &mh->coin_pub, - sizeof (struct TALER_CoinSpendPublicKeyP), - pub_str, - sizeof (pub_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "coins/%s/melt", - pub_str); - } - - key_state = mh->keys; - mh->dki = TALER_EXCHANGE_get_denomination_key (key_state, - &mh->md.melted_coin.pub_key); - - /* and now we can at last begin the actual request handling */ - - mh->url = TALER_url_join (mh->exchange_url, - arg_str, - NULL); - if (NULL == mh->url) - { - json_decref (melt_obj); - return GNUNET_SYSERR; - } - eh = TALER_EXCHANGE_curl_easy_get_ (mh->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&mh->ctx, - eh, - melt_obj)) ) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (melt_obj); - return GNUNET_SYSERR; - } - json_decref (melt_obj); - mh->job = GNUNET_CURL_job_add2 (mh->cctx, - eh, - mh->ctx.headers, - &handle_melt_finished, - mh); - return GNUNET_OK; -} - - -/** - * The melt request @a mh failed, return an error to - * the application and cancel the operation. - * - * @param[in] mh melt request that failed - * @param ec error code to fail with - */ -static void -fail_mh (struct TALER_EXCHANGE_MeltHandle *mh, - enum TALER_ErrorCode ec) -{ - struct TALER_EXCHANGE_MeltResponse mr = { - .hr.ec = ec - }; - - mh->melt_cb (mh->melt_cb_cls, - &mr); - TALER_EXCHANGE_melt_cancel (mh); -} - - -/** - * Callbacks of this type are used to serve the result of submitting a - * CS R request to a exchange. - * - * @param cls closure with our `struct TALER_EXCHANGE_MeltHandle *` - * @param csrr response details - */ -static void -csr_cb (void *cls, - const struct TALER_EXCHANGE_CsRMeltResponse *csrr) -{ - struct TALER_EXCHANGE_MeltHandle *mh = cls; - unsigned int nks_off = 0; - - mh->csr = NULL; - if (MHD_HTTP_OK != csrr->hr.http_status) - { - struct TALER_EXCHANGE_MeltResponse mr = { - .hr = csrr->hr - }; - - mr.hr.hint = "/csr-melt failed"; - mh->melt_cb (mh->melt_cb_cls, - &mr); - TALER_EXCHANGE_melt_cancel (mh); - return; - } - for (unsigned int i = 0; i<mh->rd->fresh_pks_len; i++) - { - const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk = - &mh->rd->fresh_pks[i]; - struct TALER_ExchangeBlindingValues *wv = &mh->mbds[i].alg_value; - - switch (fresh_pk->key.bsign_pub_key->cipher) - { - case GNUNET_CRYPTO_BSA_INVALID: - GNUNET_break (0); - fail_mh (mh, - TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR); - return; - case GNUNET_CRYPTO_BSA_RSA: - break; - case GNUNET_CRYPTO_BSA_CS: - TALER_denom_ewv_copy (wv, - &csrr->details.ok.blinding_values[nks_off]); - nks_off++; - break; - } - } - mh->send_rms = true; - if (GNUNET_OK != - start_melt (mh)) - { - GNUNET_break (0); - fail_mh (mh, - TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR); - return; - } -} - - -struct TALER_EXCHANGE_MeltHandle * -TALER_EXCHANGE_melt ( - struct GNUNET_CURL_Context *ctx, - const char *url, - struct TALER_EXCHANGE_Keys *keys, - const struct TALER_RefreshMasterSecretP *rms, - const struct TALER_EXCHANGE_RefreshData *rd, - TALER_EXCHANGE_MeltCallback melt_cb, - void *melt_cb_cls) -{ - struct TALER_EXCHANGE_NonceKey nks[GNUNET_NZL (rd->fresh_pks_len)]; - unsigned int nks_off = 0; - struct TALER_EXCHANGE_MeltHandle *mh; - - if (0 == rd->fresh_pks_len) - { - GNUNET_break (0); - return NULL; - } - mh = GNUNET_new (struct TALER_EXCHANGE_MeltHandle); - mh->noreveal_index = TALER_CNC_KAPPA; /* invalid value */ - mh->cctx = ctx; - mh->exchange_url = GNUNET_strdup (url); - mh->rd = rd; - mh->rms = *rms; - mh->melt_cb = melt_cb; - mh->melt_cb_cls = melt_cb_cls; - mh->mbds = GNUNET_new_array (rd->fresh_pks_len, - struct TALER_EXCHANGE_MeltBlindingDetail); - for (unsigned int i = 0; i<rd->fresh_pks_len; i++) - { - const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk = &rd->fresh_pks[i]; - - switch (fresh_pk->key.bsign_pub_key->cipher) - { - case GNUNET_CRYPTO_BSA_INVALID: - GNUNET_break (0); - GNUNET_free (mh->mbds); - GNUNET_free (mh); - return NULL; - case GNUNET_CRYPTO_BSA_RSA: - TALER_denom_ewv_copy (&mh->mbds[i].alg_value, - TALER_denom_ewv_rsa_singleton ()); - break; - case GNUNET_CRYPTO_BSA_CS: - nks[nks_off].pk = fresh_pk; - nks[nks_off].cnc_num = nks_off; - nks_off++; - break; - } - } - mh->keys = TALER_EXCHANGE_keys_incref (keys); - if (0 != nks_off) - { - mh->csr = TALER_EXCHANGE_csr_melt (ctx, - url, - rms, - nks_off, - nks, - &csr_cb, - mh); - if (NULL == mh->csr) - { - GNUNET_break (0); - TALER_EXCHANGE_melt_cancel (mh); - return NULL; - } - return mh; - } - if (GNUNET_OK != - start_melt (mh)) - { - GNUNET_break (0); - TALER_EXCHANGE_melt_cancel (mh); - return NULL; - } - return mh; -} - - -void -TALER_EXCHANGE_melt_cancel (struct TALER_EXCHANGE_MeltHandle *mh) -{ - for (unsigned int i = 0; i<mh->rd->fresh_pks_len; i++) - TALER_denom_ewv_free (&mh->mbds[i].alg_value); - if (NULL != mh->job) - { - GNUNET_CURL_job_cancel (mh->job); - mh->job = NULL; - } - if (NULL != mh->csr) - { - TALER_EXCHANGE_csr_melt_cancel (mh->csr); - mh->csr = NULL; - } - TALER_EXCHANGE_free_melt_data_ (&mh->md); /* does not free 'md' itself */ - GNUNET_free (mh->mbds); - GNUNET_free (mh->url); - GNUNET_free (mh->exchange_url); - TALER_curl_easy_post_finished (&mh->ctx); - TALER_EXCHANGE_keys_decref (mh->keys); - GNUNET_free (mh); -} - - -/* end of exchange_api_melt.c */ diff --git a/src/lib/exchange_api_refresh_common.c b/src/lib/exchange_api_refresh_common.c @@ -23,240 +23,6 @@ #include "platform.h" #include "exchange_api_refresh_common.h" - -void -TALER_EXCHANGE_free_melt_data_ (struct MeltData *md) -{ - for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++) - { - struct TALER_RefreshCoinData *rcds = md->rcd[i]; - - if (NULL == rcds) - continue; - for (unsigned int j = 0; j < md->num_fresh_coins; j++) - TALER_blinded_planchet_free (&rcds[j].blinded_planchet); - GNUNET_free (rcds); - } - TALER_denom_pub_free (&md->melted_coin.pub_key); - TALER_denom_sig_free (&md->melted_coin.sig); - if (NULL != md->fcds) - { - for (unsigned int j = 0; j<md->num_fresh_coins; j++) - { - struct FreshCoinData *fcd = &md->fcds[j]; - - TALER_denom_pub_free (&fcd->fresh_pk); - for (size_t i = 0; i < TALER_CNC_KAPPA; i++) - { - TALER_age_commitment_proof_free (fcd->age_commitment_proofs[i]); - GNUNET_free (fcd->age_commitment_proofs[i]); - } - } - GNUNET_free (md->fcds); - } - /* Finally, clean up a bit... */ - GNUNET_CRYPTO_zero_keys (md, - sizeof (struct MeltData)); -} - - -enum GNUNET_GenericReturnValue -TALER_EXCHANGE_get_melt_data_ ( - const struct TALER_RefreshMasterSecretP *rms, - const struct TALER_EXCHANGE_RefreshData *rd, - const struct TALER_ExchangeBlindingValues *alg_values, - struct MeltData *md) -{ - struct TALER_Amount total; - struct TALER_CoinSpendPublicKeyP coin_pub; - union GNUNET_CRYPTO_BlindSessionNonce nonces[rd->fresh_pks_len]; - bool uses_cs = false; - - GNUNET_CRYPTO_eddsa_key_get_public (&rd->melt_priv.eddsa_priv, - &coin_pub.eddsa_pub); - /* build up melt data structure */ - memset (md, - 0, - sizeof (*md)); - md->num_fresh_coins = rd->fresh_pks_len; - md->melted_coin.coin_priv = rd->melt_priv; - md->melted_coin.melt_amount_with_fee = rd->melt_amount; - md->melted_coin.fee_melt = rd->melt_pk.fees.refresh; - md->melted_coin.original_value = rd->melt_pk.value; - md->melted_coin.expire_deposit = rd->melt_pk.expire_deposit; - md->melted_coin.age_commitment_proof = rd->melt_age_commitment_proof; - md->melted_coin.h_age_commitment = rd->melt_h_age_commitment; - - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (rd->melt_amount.currency, - &total)); - TALER_denom_pub_copy (&md->melted_coin.pub_key, - &rd->melt_pk.key); - TALER_denom_sig_copy (&md->melted_coin.sig, - &rd->melt_sig); - md->fcds = GNUNET_new_array (md->num_fresh_coins, - struct FreshCoinData); - for (unsigned int j = 0; j<rd->fresh_pks_len; j++) - { - struct FreshCoinData *fcd = &md->fcds[j]; - - TALER_denom_pub_copy (&fcd->fresh_pk, - &rd->fresh_pks[j].key); - GNUNET_assert (NULL != fcd->fresh_pk.bsign_pub_key); - if (alg_values[j].blinding_inputs->cipher != - fcd->fresh_pk.bsign_pub_key->cipher) - { - GNUNET_break (0); - TALER_EXCHANGE_free_melt_data_ (md); - return GNUNET_SYSERR; - } - switch (fcd->fresh_pk.bsign_pub_key->cipher) - { - case GNUNET_CRYPTO_BSA_INVALID: - GNUNET_break (0); - TALER_EXCHANGE_free_melt_data_ (md); - return GNUNET_SYSERR; - case GNUNET_CRYPTO_BSA_RSA: - break; - case GNUNET_CRYPTO_BSA_CS: - uses_cs = true; - TALER_cs_refresh_nonce_derive (rms, - j, - &nonces[j].cs_nonce); - break; - } - if ( (0 > - TALER_amount_add (&total, - &total, - &rd->fresh_pks[j].value)) || - (0 > - TALER_amount_add (&total, - &total, - &rd->fresh_pks[j].fees.withdraw)) ) - { - GNUNET_break (0); - TALER_EXCHANGE_free_melt_data_ (md); - return GNUNET_SYSERR; - } - } - - /* verify that melt_amount is above total cost */ - if (1 == - TALER_amount_cmp (&total, - &rd->melt_amount) ) - { - /* Eh, this operation is more expensive than the - @a melt_amount. This is not OK. */ - GNUNET_break (0); - TALER_EXCHANGE_free_melt_data_ (md); - return GNUNET_SYSERR; - } - - /* build up coins */ - for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++) - { - struct TALER_TransferSecretP trans_sec; - - TALER_planchet_secret_to_transfer_priv ( - rms, - &rd->melt_priv, - i, - &md->transfer_priv[i]); - - GNUNET_CRYPTO_ecdhe_key_get_public ( - &md->transfer_priv[i].ecdhe_priv, - &md->transfer_pub[i].ecdhe_pub); - - TALER_link_derive_transfer_secret (&rd->melt_priv, - &md->transfer_priv[i], - &trans_sec); - - md->rcd[i] = GNUNET_new_array (rd->fresh_pks_len, - struct TALER_RefreshCoinData); - - for (unsigned int j = 0; j<rd->fresh_pks_len; j++) - { - struct FreshCoinData *fcd = &md->fcds[j]; - struct TALER_CoinSpendPrivateKeyP *coin_priv = &fcd->coin_priv; - struct TALER_PlanchetMasterSecretP *ps = &fcd->ps[i]; - struct TALER_RefreshCoinData *rcd = &md->rcd[i][j]; - union GNUNET_CRYPTO_BlindingSecretP *bks = &fcd->bks[i]; - struct TALER_PlanchetDetail pd; - struct TALER_CoinPubHashP c_hash; - struct TALER_AgeCommitmentHash ach; - struct TALER_AgeCommitmentHash *pah = NULL; - - TALER_transfer_secret_to_planchet_secret (&trans_sec, - j, - ps); - - TALER_planchet_setup_coin_priv (ps, - &alg_values[j], - coin_priv); - - TALER_planchet_blinding_secret_create (ps, - &alg_values[j], - bks); - - if (NULL != rd->melt_age_commitment_proof) - { - fcd->age_commitment_proofs[i] = GNUNET_new (struct - TALER_AgeCommitmentProof); - - GNUNET_assert (GNUNET_OK == - TALER_age_commitment_proof_derive ( - md->melted_coin.age_commitment_proof, - &trans_sec.key, - fcd->age_commitment_proofs[i])); - - TALER_age_commitment_hash ( - &fcd->age_commitment_proofs[i]->commitment, - &ach); - pah = &ach; - } - - if (GNUNET_OK != - TALER_planchet_prepare (&fcd->fresh_pk, - &alg_values[j], - bks, - &nonces[j], - coin_priv, - pah, - &c_hash, - &pd)) - { - GNUNET_break_op (0); - TALER_EXCHANGE_free_melt_data_ (md); - return GNUNET_SYSERR; - } - rcd->blinded_planchet = pd.blinded_planchet; - rcd->dk = &fcd->fresh_pk; - } - } - - /* Finally, compute refresh commitment */ - { - struct TALER_RefreshCommitmentEntry rce[TALER_CNC_KAPPA]; - - for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++) - { - rce[i].transfer_pub = md->transfer_pub[i]; - rce[i].new_coins = md->rcd[i]; - } - TALER_refresh_get_commitment (&md->rc, - TALER_CNC_KAPPA, - uses_cs - ? rms - : NULL, - rd->fresh_pks_len, - rce, - &coin_pub, - &rd->melt_amount); - } - return GNUNET_OK; -} - - void TALER_EXCHANGE_free_melt_data_v27 (struct MeltData_v27 *md) { @@ -286,7 +52,7 @@ TALER_EXCHANGE_free_melt_data_v27 (struct MeltData_v27 *md) } /* Finally, clean up a bit... */ GNUNET_CRYPTO_zero_keys (md, - sizeof (struct MeltData)); + sizeof (struct MeltData_v27)); } diff --git a/src/lib/exchange_api_refresh_common.h b/src/lib/exchange_api_refresh_common.h @@ -117,62 +117,6 @@ struct FreshCoinData /** * Melt data in non-serialized format for convenient processing. */ -struct MeltData -{ - - /** - * Hash over the committed data during refresh operation. - */ - struct TALER_RefreshCommitmentP rc; - - /** - * Information about the melted coin. - */ - struct MeltedCoin melted_coin; - - /** - * Array of length @e num_fresh_coins with information - * about each fresh coin. - */ - struct FreshCoinData *fcds; - - /** - * Transfer secrets, one per cut and choose. - */ - struct TALER_TransferSecretP trans_sec[TALER_CNC_KAPPA]; - - /** - * Transfer private keys for each cut-and-choose dimension. - */ - struct TALER_TransferPrivateKeyP transfer_priv[TALER_CNC_KAPPA]; - - /** - * Transfer public key of this commitment. - */ - struct TALER_TransferPublicKeyP transfer_pub[TALER_CNC_KAPPA]; - - /** - * Transfer secrets, one per cut and choose. - */ - struct TALER_RefreshCommitmentEntry rce[TALER_CNC_KAPPA]; - - /** - * Blinded planchets and denominations of the fresh coins, depending on the cut-and-choose. Array of length - * @e num_fresh_coins. - */ - struct TALER_RefreshCoinData *rcd[TALER_CNC_KAPPA]; - - /** - * Number of coins we are creating - */ - uint16_t num_fresh_coins; - -}; - - -/** - * Melt data in non-serialized format for convenient processing. - */ struct MeltData_v27 { @@ -245,21 +189,6 @@ struct MeltData_v27 }; /** - * Compute the melt data from the refresh data and secret. - * - * @param rms secret internals of the refresh-reveal operation - * @param rd refresh data with the characteristics of the operation - * @param alg_values contributions from the exchange into the melt - * @param[out] md where to write the derived melt data - */ -enum GNUNET_GenericReturnValue -TALER_EXCHANGE_get_melt_data_ ( - const struct TALER_RefreshMasterSecretP *rms, - const struct TALER_EXCHANGE_RefreshData *rd, - const struct TALER_ExchangeBlindingValues *alg_values, - struct MeltData *md); - -/** * Compute the melt data from the refresh data and secret * for v27 of the protocol. * @@ -277,20 +206,6 @@ TALER_EXCHANGE_get_melt_data_v27 ( const struct TALER_ExchangeBlindingValues *alg_values, struct MeltData_v27 *md); - -/** - * Free all information associated with a melting session. Note - * that we allow the melting session to be only partially initialized, - * as we use this function also when freeing melt data that was not - * fully initialized. - * - * @param[in] md melting data to release, the pointer itself is NOT - * freed (as it is typically not allocated by itself) - */ -void -TALER_EXCHANGE_free_melt_data_ (struct MeltData *md); - - /** * Free all information associated with a melting session. Note * that we allow the melting session to be only partially initialized, diff --git a/src/lib/exchange_api_refreshes_reveal.c b/src/lib/exchange_api_refreshes_reveal.c @@ -1,533 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2015-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 - <http://www.gnu.org/licenses/> -*/ -/** - * @file lib/exchange_api_refreshes_reveal.c - * @brief Implementation of the /refreshes/$RCH/reveal requests - * @author Christian Grothoff - */ -#include "platform.h" -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_json_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler_json_lib.h" -#include "taler_exchange_service.h" -#include "exchange_api_handle.h" -#include "taler_signatures.h" -#include "exchange_api_curl_defaults.h" -#include "exchange_api_refresh_common.h" - - -/** - * @brief A /refreshes/$RCH/reveal Handle - */ -struct TALER_EXCHANGE_RefreshesRevealHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Context for #TEH_curl_easy_post(). Keeps the data that must - * persist for Curl to make the upload. - */ - struct TALER_CURL_PostContext ctx; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Exchange-contributed values to the operation. - */ - struct TALER_ExchangeBlindingValues *alg_values; - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_RefreshesRevealCallback reveal_cb; - - /** - * Closure for @e reveal_cb. - */ - void *reveal_cb_cls; - - /** - * Actual information about the melt operation. - */ - struct MeltData md; - - /** - * The index selected by the exchange in cut-and-choose to not be revealed. - */ - uint16_t noreveal_index; - -}; - - -/** - * We got a 200 OK response for the /refreshes/$RCH/reveal operation. Extract - * the coin signatures and return them to the caller. The signatures we get - * from the exchange is for the blinded value. Thus, we first must unblind - * them and then should verify their validity. - * - * If everything checks out, we return the unblinded signatures - * to the application via the callback. - * - * @param rrh operation handle - * @param json reply from the exchange - * @param[out] rcis array of length `num_fresh_coins`, initialized to contain the coin data - * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors - */ -static enum GNUNET_GenericReturnValue -refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh, - const json_t *json, - struct TALER_EXCHANGE_RevealedCoinInfo *rcis) -{ - const json_t *jsona; - struct GNUNET_JSON_Specification outer_spec[] = { - GNUNET_JSON_spec_array_const ("ev_sigs", - &jsona), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - outer_spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (rrh->md.num_fresh_coins != json_array_size (jsona)) - { - /* Number of coins generated does not match our expectation */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - for (unsigned int i = 0; i<rrh->md.num_fresh_coins; i++) - { - struct TALER_EXCHANGE_RevealedCoinInfo *rci = &rcis[i]; - const struct FreshCoinData *fcd = &rrh->md.fcds[i]; - const struct TALER_DenominationPublicKey *pk; - json_t *jsonai; - struct TALER_BlindedDenominationSignature blind_sig; - struct TALER_CoinSpendPublicKeyP coin_pub; - struct TALER_CoinPubHashP coin_hash; - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_blinded_denom_sig ("ev_sig", - &blind_sig), - GNUNET_JSON_spec_end () - }; - struct TALER_FreshCoin coin; - union GNUNET_CRYPTO_BlindingSecretP bks; - const struct TALER_AgeCommitmentHash *pah = NULL; - - rci->ps = fcd->ps[rrh->noreveal_index]; - rci->bks = fcd->bks[rrh->noreveal_index]; - rci->age_commitment_proof = NULL; - pk = &fcd->fresh_pk; - jsonai = json_array_get (jsona, i); - GNUNET_assert (NULL != jsonai); - if (NULL != rrh->md.melted_coin.age_commitment_proof) - { - rci->age_commitment_proof - = fcd->age_commitment_proofs[rrh->noreveal_index]; - TALER_age_commitment_hash (&rci->age_commitment_proof->commitment, - &rci->h_age_commitment); - pah = &rci->h_age_commitment; - } - - if (GNUNET_OK != - GNUNET_JSON_parse (jsonai, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - TALER_planchet_setup_coin_priv (&rci->ps, - &rrh->alg_values[i], - &rci->coin_priv); - TALER_planchet_blinding_secret_create (&rci->ps, - &rrh->alg_values[i], - &bks); - /* needed to verify the signature, and we didn't store it earlier, - hence recomputing it here... */ - GNUNET_CRYPTO_eddsa_key_get_public (&rci->coin_priv.eddsa_priv, - &coin_pub.eddsa_pub); - TALER_coin_pub_hash ( - &coin_pub, - pah, - &coin_hash); - if (GNUNET_OK != - TALER_planchet_to_coin ( - pk, - &blind_sig, - &bks, - &rci->coin_priv, - pah, - &coin_hash, - &rrh->alg_values[i], - &coin)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return GNUNET_SYSERR; - } - GNUNET_JSON_parse_free (spec); - rci->sig = coin.sig; - } - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP /refreshes/$RCH/reveal request. - * - * @param cls the `struct TALER_EXCHANGE_RefreshHandle` - * @param response_code HTTP response code, 0 on error - * @param response parsed JSON result, NULL on error - */ -static void -handle_refresh_reveal_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_RefreshesRevealHandle *rrh = cls; - const json_t *j = response; - struct TALER_EXCHANGE_RevealResult rr = { - .hr.reply = j, - .hr.http_status = (unsigned int) response_code - }; - - rrh->job = NULL; - switch (response_code) - { - case 0: - rr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - { - struct TALER_EXCHANGE_RevealedCoinInfo rcis[rrh->md.num_fresh_coins]; - enum GNUNET_GenericReturnValue ret; - - memset (rcis, - 0, - sizeof (rcis)); - ret = refresh_reveal_ok (rrh, - j, - rcis); - if (GNUNET_OK != ret) - { - rr.hr.http_status = 0; - rr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - break; - } - else - { - GNUNET_assert (rrh->noreveal_index < TALER_CNC_KAPPA); - rr.details.ok.num_coins = rrh->md.num_fresh_coins; - rr.details.ok.coins = rcis; - rrh->reveal_cb (rrh->reveal_cb_cls, - &rr); - rrh->reveal_cb = NULL; - } - for (unsigned int i = 0; i<rrh->md.num_fresh_coins; i++) - { - TALER_denom_sig_free (&rcis[i].sig); - TALER_age_commitment_proof_free (rcis[i].age_commitment_proof); - } - TALER_EXCHANGE_refreshes_reveal_cancel (rrh); - return; - } - case MHD_HTTP_BAD_REQUEST: - /* This should never happen, either us or the exchange is buggy - (or API version conflict); just pass JSON reply to the application */ - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_CONFLICT: - /* Nothing really to verify, exchange says our reveal is inconsistent - with our commitment, so either side is buggy; we - should pass the JSON reply to the application */ - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_GONE: - /* Server claims key expired or has been revoked */ - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange refreshes reveal\n", - (unsigned int) response_code, - (int) rr.hr.ec); - break; - } - if (NULL != rrh->reveal_cb) - rrh->reveal_cb (rrh->reveal_cb_cls, - &rr); - TALER_EXCHANGE_refreshes_reveal_cancel (rrh); -} - - -struct TALER_EXCHANGE_RefreshesRevealHandle * -TALER_EXCHANGE_refreshes_reveal ( - struct GNUNET_CURL_Context *ctx, - const char *url, - const struct TALER_RefreshMasterSecretP *rms, - const struct TALER_EXCHANGE_RefreshData *rd, - unsigned int num_coins, - const struct TALER_ExchangeBlindingValues alg_values[static num_coins], - uint32_t noreveal_index, - TALER_EXCHANGE_RefreshesRevealCallback reveal_cb, - void *reveal_cb_cls) -{ - struct TALER_EXCHANGE_RefreshesRevealHandle *rrh; - json_t *transfer_privs; - json_t *new_denoms_h; - json_t *coin_evs; - json_t *reveal_obj; - json_t *link_sigs; - json_t *old_age_commitment = NULL; - CURL *eh; - struct MeltData md; - char arg_str[sizeof (struct TALER_RefreshCommitmentP) * 2 + 32]; - bool send_rms = false; - - GNUNET_assert (num_coins == rd->fresh_pks_len); - if (noreveal_index >= TALER_CNC_KAPPA) - { - /* We check this here, as it would be really bad to below just - disclose all the transfer keys. Note that this error should - have been caught way earlier when the exchange replied, but maybe - we had some internal corruption that changed the value... */ - GNUNET_break (0); - return NULL; - } - if (GNUNET_OK != - TALER_EXCHANGE_get_melt_data_ (rms, - rd, - alg_values, - &md)) - { - GNUNET_break (0); - return NULL; - } - - /* now new_denoms */ - GNUNET_assert (NULL != (new_denoms_h = json_array ())); - GNUNET_assert (NULL != (coin_evs = json_array ())); - GNUNET_assert (NULL != (link_sigs = json_array ())); - for (unsigned int i = 0; i<md.num_fresh_coins; i++) - { - const struct TALER_RefreshCoinData *rcd = &md.rcd[noreveal_index][i]; - struct TALER_DenominationHashP denom_hash; - - if (GNUNET_CRYPTO_BSA_CS == - md.fcds[i].fresh_pk.bsign_pub_key->cipher) - send_rms = true; - TALER_denom_pub_hash (&md.fcds[i].fresh_pk, - &denom_hash); - GNUNET_assert (0 == - json_array_append_new (new_denoms_h, - GNUNET_JSON_from_data_auto ( - &denom_hash))); - GNUNET_assert (0 == - json_array_append_new ( - coin_evs, - GNUNET_JSON_PACK ( - TALER_JSON_pack_blinded_planchet ( - NULL, - &rcd->blinded_planchet)))); - { - struct TALER_CoinSpendSignatureP link_sig; - struct TALER_BlindedCoinHashP bch; - - TALER_coin_ev_hash (&rcd->blinded_planchet, - &denom_hash, - &bch); - TALER_wallet_link_sign ( - &denom_hash, - &md.transfer_pub[noreveal_index], - &bch, - &md.melted_coin.coin_priv, - &link_sig); - GNUNET_assert (0 == - json_array_append_new ( - link_sigs, - GNUNET_JSON_from_data_auto (&link_sig))); - } - } - - /* build array of transfer private keys */ - GNUNET_assert (NULL != (transfer_privs = json_array ())); - for (unsigned int j = 0; j<TALER_CNC_KAPPA; j++) - { - if (j == noreveal_index) - { - /* This is crucial: exclude the transfer key for the noreval index! */ - continue; - } - GNUNET_assert (0 == - json_array_append_new (transfer_privs, - GNUNET_JSON_from_data_auto ( - &md.transfer_priv[j]))); - } - - /* build array of old age commitment, if applicable */ - if (NULL != rd->melt_age_commitment_proof) - { - GNUNET_assert (NULL != rd->melt_h_age_commitment); - GNUNET_assert (NULL != (old_age_commitment = json_array ())); - - for (size_t i = 0; i < rd->melt_age_commitment_proof->commitment.num; i++) - { - enum GNUNET_GenericReturnValue ret; - - ret = json_array_append_new ( - old_age_commitment, - GNUNET_JSON_from_data_auto ( - &rd->melt_age_commitment_proof->commitment.pubs[i])); - GNUNET_assert (0 == ret); - } - } - - /* build main JSON request */ - reveal_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("transfer_pub", - &md.transfer_pub[noreveal_index]), - GNUNET_JSON_pack_allow_null ( - send_rms - ? GNUNET_JSON_pack_data_auto ("rms", - rms) - : GNUNET_JSON_pack_string ("rms", - NULL)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_array_steal ("old_age_commitment", - old_age_commitment)), - GNUNET_JSON_pack_array_steal ("transfer_privs", - transfer_privs), - GNUNET_JSON_pack_array_steal ("link_sigs", - link_sigs), - GNUNET_JSON_pack_array_steal ("new_denoms_h", - new_denoms_h), - GNUNET_JSON_pack_array_steal ("coin_evs", - coin_evs)); - { - char pub_str[sizeof (struct TALER_RefreshCommitmentP) * 2]; - char *end; - - end = GNUNET_STRINGS_data_to_string (&md.rc, - sizeof (md.rc), - pub_str, - sizeof (pub_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "refreshes/%s/reveal", - pub_str); - } - /* finally, we can actually issue the request */ - rrh = GNUNET_new (struct TALER_EXCHANGE_RefreshesRevealHandle); - rrh->noreveal_index = noreveal_index; - rrh->reveal_cb = reveal_cb; - rrh->reveal_cb_cls = reveal_cb_cls; - rrh->md = md; - rrh->alg_values - = GNUNET_new_array (md.num_fresh_coins, - struct TALER_ExchangeBlindingValues); - for (unsigned int i = 0; i<md.num_fresh_coins; i++) - TALER_denom_ewv_copy (&rrh->alg_values[i], - &alg_values[i]); - rrh->url = TALER_url_join (url, - arg_str, - NULL); - if (NULL == rrh->url) - { - json_decref (reveal_obj); - TALER_EXCHANGE_free_melt_data_ (&md); - GNUNET_free (rrh->alg_values); - GNUNET_free (rrh); - return NULL; - } - - eh = TALER_EXCHANGE_curl_easy_get_ (rrh->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&rrh->ctx, - eh, - reveal_obj)) ) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (reveal_obj); - TALER_EXCHANGE_free_melt_data_ (&md); - GNUNET_free (rrh->alg_values); - GNUNET_free (rrh->url); - GNUNET_free (rrh); - return NULL; - } - json_decref (reveal_obj); - rrh->job = GNUNET_CURL_job_add2 (ctx, - eh, - rrh->ctx.headers, - &handle_refresh_reveal_finished, - rrh); - return rrh; -} - - -void -TALER_EXCHANGE_refreshes_reveal_cancel ( - struct TALER_EXCHANGE_RefreshesRevealHandle *rrh) -{ - if (NULL != rrh->job) - { - GNUNET_CURL_job_cancel (rrh->job); - rrh->job = NULL; - } - for (unsigned int i = 0; i<rrh->md.num_fresh_coins; i++) - TALER_denom_ewv_free (&rrh->alg_values[i]); - GNUNET_free (rrh->alg_values); - GNUNET_free (rrh->url); - TALER_curl_easy_post_finished (&rrh->ctx); - TALER_EXCHANGE_free_melt_data_ (&rrh->md); - GNUNET_free (rrh); -} - - -/* exchange_api_refreshes_reveal.c */