summaryrefslogtreecommitdiff
path: root/src/exchange
diff options
context:
space:
mode:
authorChristian Grothoff <grothoff@gnunet.org>2023-10-15 23:30:51 +0200
committerChristian Grothoff <grothoff@gnunet.org>2023-10-15 23:30:51 +0200
commit917dd4d70ff2f38d475146b387e649a669996f10 (patch)
tree2550f23c5d9418bdaae7c5b7b328d9e5c60e226b /src/exchange
parentc72cf2ce10017dccc342c4ea86ac2b006aa54149 (diff)
downloadexchange-917dd4d70ff2f38d475146b387e649a669996f10.tar.gz
exchange-917dd4d70ff2f38d475146b387e649a669996f10.tar.bz2
exchange-917dd4d70ff2f38d475146b387e649a669996f10.zip
avoid extra transaction to fetch balance if reserve is out of funds, remove legacy /withdraw endpoint
Diffstat (limited to 'src/exchange')
-rw-r--r--src/exchange/Makefile.am3
-rw-r--r--src/exchange/taler-exchange-httpd.c5
-rw-r--r--src/exchange/taler-exchange-httpd_age-withdraw.c15
-rw-r--r--src/exchange/taler-exchange-httpd_batch-withdraw.c5
-rw-r--r--src/exchange/taler-exchange-httpd_reserves_open.c3
-rw-r--r--src/exchange/taler-exchange-httpd_responses.c19
-rw-r--r--src/exchange/taler-exchange-httpd_responses.h2
-rw-r--r--src/exchange/taler-exchange-httpd_withdraw.c700
-rw-r--r--src/exchange/taler-exchange-httpd_withdraw.h47
9 files changed, 21 insertions, 778 deletions
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index 86829edda..12ea34e13 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -182,8 +182,7 @@ taler_exchange_httpd_SOURCES = \
taler-exchange-httpd_reserves_purse.c taler-exchange-httpd_reserves_purse.h \
taler-exchange-httpd_responses.c taler-exchange-httpd_responses.h \
taler-exchange-httpd_terms.c taler-exchange-httpd_terms.h \
- taler-exchange-httpd_transfers_get.c taler-exchange-httpd_transfers_get.h \
- taler-exchange-httpd_withdraw.c taler-exchange-httpd_withdraw.h
+ taler-exchange-httpd_transfers_get.c taler-exchange-httpd_transfers_get.h
taler_exchange_httpd_LDADD = \
$(LIBGCRYPT_LIBS) \
diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c
index 5bc118e3e..46d5833a9 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -69,7 +69,6 @@
#include "taler-exchange-httpd_reserves_purse.h"
#include "taler-exchange-httpd_terms.h"
#include "taler-exchange-httpd_transfers_get.h"
-#include "taler-exchange-httpd_withdraw.h"
#include "taler_exchangedb_lib.h"
#include "taler_exchangedb_plugin.h"
#include "taler_extensions.h"
@@ -747,10 +746,6 @@ handle_post_reserves (struct TEH_RequestContext *rc,
.handler = &TEH_handler_age_withdraw
},
{
- .op = "withdraw",
- .handler = &TEH_handler_withdraw
- },
- {
.op = "purse",
.handler = &TEH_handler_reserves_purse
},
diff --git a/src/exchange/taler-exchange-httpd_age-withdraw.c b/src/exchange/taler-exchange-httpd_age-withdraw.c
index 47cff626f..4a7d6b1a8 100644
--- a/src/exchange/taler-exchange-httpd_age-withdraw.c
+++ b/src/exchange/taler-exchange-httpd_age-withdraw.c
@@ -399,8 +399,8 @@ denomination_is_valid (
* @param[out] denom_serials On success, will be filled with the serial-id's of the denomination keys. Caller must deallocate.
* @param[out] amount_with_fee On success, will contain the committed amount including fees
* @param[out] result In the error cases, a response will be queued with MHD and this will be the result.
- * @return GNUNET_OK if the denominations are valid and support age-restriction
- * GNUNET_SYSERR otherwise
+ * @return #GNUNET_OK if the denominations are valid and support age-restriction
+ * #GNUNET_SYSERR otherwise
*/
static enum GNUNET_GenericReturnValue
are_denominations_valid (
@@ -737,12 +737,14 @@ age_withdraw_transaction (void *cls,
bool conflict = false;
uint16_t allowed_maximum_age = 0;
uint32_t reserve_birthday = 0;
+ struct TALER_Amount reserve_balance;
qs = TEH_plugin->do_age_withdraw (TEH_plugin->cls,
&awc->commitment,
awc->now,
&found,
&balance_ok,
+ &reserve_balance,
&age_ok,
&allowed_maximum_age,
&reserve_birthday,
@@ -755,14 +757,14 @@ age_withdraw_transaction (void *cls,
"do_age_withdraw");
return qs;
}
- else if (! found)
+ if (! found)
{
*mhd_ret = TALER_MHD_reply_with_ec (connection,
TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
NULL);
return GNUNET_DB_STATUS_HARD_ERROR;
}
- else if (! age_ok)
+ if (! age_ok)
{
enum TALER_ErrorCode ec =
TALER_EC_EXCHANGE_AGE_WITHDRAW_MAXIMUM_AGE_TOO_LARGE;
@@ -779,19 +781,20 @@ age_withdraw_transaction (void *cls,
return GNUNET_DB_STATUS_HARD_ERROR;
}
- else if (! balance_ok)
+ if (! balance_ok)
{
TEH_plugin->rollback (TEH_plugin->cls);
*mhd_ret = TEH_RESPONSE_reply_reserve_insufficient_balance (
connection,
TALER_EC_EXCHANGE_AGE_WITHDRAW_INSUFFICIENT_FUNDS,
+ &reserve_balance,
&awc->commitment.amount_with_fee,
&awc->commitment.reserve_pub);
return GNUNET_DB_STATUS_HARD_ERROR;
}
- else if (conflict)
+ if (conflict)
{
/* do_age_withdraw signaled a conflict, so there MUST be an entry
* in the DB. Put that into the response */
diff --git a/src/exchange/taler-exchange-httpd_batch-withdraw.c b/src/exchange/taler-exchange-httpd_batch-withdraw.c
index 38a7f43c5..fe2108763 100644
--- a/src/exchange/taler-exchange-httpd_batch-withdraw.c
+++ b/src/exchange/taler-exchange-httpd_batch-withdraw.c
@@ -312,6 +312,7 @@ batch_withdraw_transaction (void *cls,
bool balance_ok = false;
bool age_ok = false;
uint16_t allowed_maximum_age = 0;
+ struct TALER_Amount reserve_balance;
char *kyc_required;
struct TALER_PaytoHashP reserve_h_payto;
@@ -476,6 +477,7 @@ batch_withdraw_transaction (void *cls,
TEH_age_restriction_enabled,
&found,
&balance_ok,
+ &reserve_balance,
&age_ok,
&allowed_maximum_age,
&ruuid);
@@ -521,6 +523,7 @@ batch_withdraw_transaction (void *cls,
*mhd_ret = TEH_RESPONSE_reply_reserve_insufficient_balance (
connection,
TALER_EC_EXCHANGE_WITHDRAW_INSUFFICIENT_FUNDS,
+ &reserve_balance,
&wc->batch_total,
wc->reserve_pub);
return GNUNET_DB_STATUS_HARD_ERROR;
@@ -553,7 +556,7 @@ batch_withdraw_transaction (void *cls,
*mhd_ret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_FETCH_FAILED,
- "do_withdraw");
+ "do_batch_withdraw_insert");
return qs;
}
if (denom_unknown)
diff --git a/src/exchange/taler-exchange-httpd_reserves_open.c b/src/exchange/taler-exchange-httpd_reserves_open.c
index 50487990a..5aadc9e40 100644
--- a/src/exchange/taler-exchange-httpd_reserves_open.c
+++ b/src/exchange/taler-exchange-httpd_reserves_open.c
@@ -188,6 +188,7 @@ reserve_open_transaction (void *cls,
{
struct ReserveOpenContext *rsc = cls;
enum GNUNET_DB_QueryStatus qs;
+ struct TALER_Amount reserve_balance;
for (unsigned int i = 0; i<rsc->payments_len; i++)
{
@@ -258,6 +259,7 @@ reserve_open_transaction (void *cls,
&rsc->gf->fees.account,
/* outputs */
&rsc->no_funds,
+ &reserve_balance,
&rsc->open_cost,
&rsc->reserve_expiration);
switch (qs)
@@ -289,6 +291,7 @@ reserve_open_transaction (void *cls,
= TEH_RESPONSE_reply_reserve_insufficient_balance (
connection,
TALER_EC_EXCHANGE_RESERVES_OPEN_INSUFFICIENT_FUNDS,
+ &reserve_balance,
&rsc->reserve_payment,
rsc->reserve_pub);
return GNUNET_DB_STATUS_HARD_ERROR;
diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c
index 2d5d8dce9..322da3877 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -181,31 +181,16 @@ MHD_RESULT
TEH_RESPONSE_reply_reserve_insufficient_balance (
struct MHD_Connection *connection,
enum TALER_ErrorCode ec,
+ const struct TALER_Amount *reserve_balance,
const struct TALER_Amount *balance_required,
const struct TALER_ReservePublicKeyP *reserve_pub)
{
- struct TALER_Amount balance;
- enum GNUNET_DB_QueryStatus qs;
-
- // FIXME: pass balance as argument to this function,
- // instead of getting it in another transaction!
- qs = TEH_plugin->get_reserve_balance (TEH_plugin->cls,
- reserve_pub,
- &balance);
- if (qs < 0)
- {
- GNUNET_break (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "reserve balance");
- }
return TALER_MHD_REPLY_JSON_PACK (
connection,
MHD_HTTP_CONFLICT,
TALER_JSON_pack_ec (ec),
TALER_JSON_pack_amount ("balance",
- &balance),
+ reserve_balance),
TALER_JSON_pack_amount ("requested_amount",
balance_required));
}
diff --git a/src/exchange/taler-exchange-httpd_responses.h b/src/exchange/taler-exchange-httpd_responses.h
index 877e8878f..8adf1136b 100644
--- a/src/exchange/taler-exchange-httpd_responses.h
+++ b/src/exchange/taler-exchange-httpd_responses.h
@@ -53,6 +53,7 @@ TEH_RESPONSE_reply_unknown_denom_pub_hash (
*
* @param connection connection to the client
* @param ec specific error code to return with the reserve history
+ * @param reserve_balance balance remaining in the reserve
* @param balance_required the balance required for the operation
* @param reserve_pub the reserve with insufficient balance
* @return MHD result code
@@ -61,6 +62,7 @@ MHD_RESULT
TEH_RESPONSE_reply_reserve_insufficient_balance (
struct MHD_Connection *connection,
enum TALER_ErrorCode ec,
+ const struct TALER_Amount *reserve_balance,
const struct TALER_Amount *balance_required,
const struct TALER_ReservePublicKeyP *reserve_pub);
diff --git a/src/exchange/taler-exchange-httpd_withdraw.c b/src/exchange/taler-exchange-httpd_withdraw.c
deleted file mode 100644
index 07fcc8464..000000000
--- a/src/exchange/taler-exchange-httpd_withdraw.c
+++ /dev/null
@@ -1,700 +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_withdraw.c
- * @brief Handle /reserves/$RESERVE_PUB/withdraw requests
- * @author Florian Dold
- * @author Benedikt Mueller
- * @author Christian Grothoff
- */
-#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_withdraw.h"
-#include "taler-exchange-httpd_responses.h"
-#include "taler-exchange-httpd_keys.h"
-
-
-/**
- * Context for #withdraw_transaction.
- */
-struct WithdrawContext
-{
-
- /**
- * Hash of the (blinded) message to be signed by the Exchange.
- */
- struct TALER_BlindedCoinHashP h_coin_envelope;
-
- /**
- * 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;
-
- /**
- * KYC status for the operation.
- */
- struct TALER_EXCHANGEDB_KycStatus kyc;
-
- /**
- * Hash of the payto-URI representing the account
- * from which the money was put into the reserve.
- */
- struct TALER_PaytoHashP h_account_payto;
-
- /**
- * Current time for the DB transaction.
- */
- struct GNUNET_TIME_Timestamp now;
-
- /**
- * AML decision, #TALER_AML_NORMAL if we may proceed.
- */
- enum TALER_AmlDecisionState aml_decision;
-
-};
-
-
-/**
- * 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
- */
-static void
-withdraw_amount_cb (void *cls,
- struct GNUNET_TIME_Absolute limit,
- TALER_EXCHANGEDB_KycAmountCallback cb,
- void *cb_cls)
-{
- struct WithdrawContext *wc = cls;
- enum GNUNET_DB_QueryStatus qs;
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Signaling amount %s for KYC check\n",
- TALER_amount2s (&wc->collectable.amount_with_fee));
- if (GNUNET_OK !=
- cb (cb_cls,
- &wc->collectable.amount_with_fee,
- wc->now.abs_time))
- return;
- qs = TEH_plugin->select_withdraw_amounts_for_kyc_check (
- TEH_plugin->cls,
- &wc->h_account_payto,
- limit,
- cb,
- cb_cls);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Got %d additional transactions for this withdrawal and limit %llu\n",
- qs,
- (unsigned long long) limit.abs_value_us);
- GNUNET_break (qs >= 0);
-}
-
-
-/**
- * Function called on each @a amount that was found to
- * be relevant for the AML check as it was merged into
- * the reserve.
- *
- * @param cls `struct TALER_Amount *` to total up the amounts
- * @param amount encountered transaction amount
- * @param date when was the amount encountered
- * @return #GNUNET_OK to continue to iterate,
- * #GNUNET_NO to abort iteration
- * #GNUNET_SYSERR on internal error (also abort itaration)
- */
-static enum GNUNET_GenericReturnValue
-aml_amount_cb (
- void *cls,
- const struct TALER_Amount *amount,
- struct GNUNET_TIME_Absolute date)
-{
- struct TALER_Amount *total = cls;
-
- GNUNET_assert (0 <=
- TALER_amount_add (total,
- total,
- amount));
- return GNUNET_OK;
-}
-
-
-/**
- * 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 WithdrawContext *`
- * @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
-withdraw_transaction (void *cls,
- struct MHD_Connection *connection,
- MHD_RESULT *mhd_ret)
-{
- struct WithdrawContext *wc = cls;
- enum GNUNET_DB_QueryStatus qs;
- bool found = false;
- bool balance_ok = false;
- bool nonce_ok = false;
- bool age_ok = false;
- uint16_t allowed_maximum_age = 0;
- uint64_t ruuid;
- const struct TALER_CsNonce *nonce;
- const struct TALER_BlindedPlanchet *bp;
- struct TALER_PaytoHashP reserve_h_payto;
-
- wc->now = GNUNET_TIME_timestamp_get ();
- /* Do AML check: compute total merged amount and check
- against applicable AML threshold */
- {
- char *reserve_payto;
-
- reserve_payto = TALER_reserve_make_payto (TEH_base_url,
- &wc->collectable.reserve_pub);
- TALER_payto_hash (reserve_payto,
- &reserve_h_payto);
- GNUNET_free (reserve_payto);
- }
- {
- struct TALER_Amount merge_amount;
- struct TALER_Amount threshold;
- struct GNUNET_TIME_Absolute now_minus_one_month;
-
- now_minus_one_month
- = GNUNET_TIME_absolute_subtract (wc->now.abs_time,
- GNUNET_TIME_UNIT_MONTHS);
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (TEH_currency,
- &merge_amount));
- qs = TEH_plugin->select_merge_amounts_for_kyc_check (TEH_plugin->cls,
- &reserve_h_payto,
- now_minus_one_month,
- &aml_amount_cb,
- &merge_amount);
- if (qs < 0)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- if (GNUNET_DB_STATUS_HARD_ERROR == qs)
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "select_merge_amounts_for_kyc_check");
- return qs;
- }
- qs = TEH_plugin->select_aml_threshold (TEH_plugin->cls,
- &reserve_h_payto,
- &wc->aml_decision,
- &wc->kyc,
- &threshold);
- if (qs < 0)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- if (GNUNET_DB_STATUS_HARD_ERROR == qs)
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "select_aml_threshold");
- return qs;
- }
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- {
- threshold = TEH_aml_threshold; /* use default */
- wc->aml_decision = TALER_AML_NORMAL;
- }
-
- switch (wc->aml_decision)
- {
- case TALER_AML_NORMAL:
- if (0 >= TALER_amount_cmp (&merge_amount,
- &threshold))
- {
- /* merge_amount <= threshold, continue withdraw below */
- break;
- }
- wc->aml_decision = TALER_AML_PENDING;
- qs = TEH_plugin->trigger_aml_process (TEH_plugin->cls,
- &reserve_h_payto,
- &merge_amount);
- if (qs <= 0)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- if (GNUNET_DB_STATUS_HARD_ERROR == qs)
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_STORE_FAILED,
- "trigger_aml_process");
- return qs;
- }
- return qs;
- case TALER_AML_PENDING:
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "AML already pending, doing nothing\n");
- return qs;
- case TALER_AML_FROZEN:
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Account frozen, doing nothing\n");
- return qs;
- }
- }
-
- /* Check if the money came from a wire transfer */
- qs = TEH_plugin->reserves_get_origin (TEH_plugin->cls,
- &wc->collectable.reserve_pub,
- &wc->h_account_payto);
- if (qs < 0)
- return qs;
- /* 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_ONE_RESULT == qs)
- {
- char *kyc_required;
-
- qs = TALER_KYCLOGIC_kyc_test_required (
- TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW,
- &wc->h_account_payto,
- TEH_plugin->select_satisfied_kyc_processes,
- TEH_plugin->cls,
- &withdraw_amount_cb,
- wc,
- &kyc_required);
- if (qs < 0)
- {
- if (GNUNET_DB_STATUS_HARD_ERROR == qs)
- {
- GNUNET_break (0);
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "kyc_test_required");
- }
- return qs;
- }
- if (NULL != kyc_required)
- {
- /* insert KYC requirement into DB! */
- wc->kyc.ok = false;
- qs = TEH_plugin->insert_kyc_requirement_for_account (
- TEH_plugin->cls,
- kyc_required,
- &wc->h_account_payto,
- &wc->collectable.reserve_pub,
- &wc->kyc.requirement_row);
- GNUNET_free (kyc_required);
- if (GNUNET_DB_STATUS_HARD_ERROR == qs)
- {
- GNUNET_break (0);
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_STORE_FAILED,
- "insert_kyc_requirement_for_account");
- }
- return qs;
- }
- }
- wc->kyc.ok = true;
- bp = &wc->blinded_planchet;
- nonce = (TALER_DENOMINATION_CS == bp->cipher)
- ? &bp->details.cs_blinded_planchet.nonce
- : NULL;
- qs = TEH_plugin->do_withdraw (TEH_plugin->cls,
- nonce,
- &wc->collectable,
- wc->now,
- TEH_age_restriction_enabled,
- &found,
- &balance_ok,
- &nonce_ok,
- &age_ok,
- &allowed_maximum_age,
- &ruuid);
- if (0 > qs)
- {
- if (GNUNET_DB_STATUS_HARD_ERROR == qs)
- {
- GNUNET_break (0);
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "do_withdraw");
- }
- return qs;
- }
- if (! found)
- {
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
- NULL);
- 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);
-
- TEH_plugin->rollback (TEH_plugin->cls);
- *mhd_ret = TEH_RESPONSE_reply_reserve_age_restriction_required (
- connection,
- lowest_age);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- if (! balance_ok)
- {
- TEH_plugin->rollback (TEH_plugin->cls);
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Balance insufficient for /withdraw\n");
- *mhd_ret = TEH_RESPONSE_reply_reserve_insufficient_balance (
- connection,
- TALER_EC_EXCHANGE_WITHDRAW_INSUFFICIENT_FUNDS,
- &wc->collectable.amount_with_fee,
- &wc->collectable.reserve_pub);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- if (! nonce_ok)
- {
- TEH_plugin->rollback (TEH_plugin->cls);
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_EXCHANGE_WITHDRAW_NONCE_REUSE,
- NULL);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
- TEH_METRICS_num_success[TEH_MT_SUCCESS_WITHDRAW]++;
- return qs;
-}
-
-
-/**
- * Check if the @a rc is replayed and we already have an
- * answer. If so, replay the existing answer and return the
- * HTTP response.
- *
- * @param rc request context
- * @param[in,out] wc parsed request data
- * @param[out] mret HTTP status, set if we return true
- * @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_request_idempotent (struct TEH_RequestContext *rc,
- struct WithdrawContext *wc,
- MHD_RESULT *mret)
-{
- enum GNUNET_DB_QueryStatus qs;
-
- qs = TEH_plugin->get_withdraw_info (TEH_plugin->cls,
- &wc->h_coin_envelope,
- &wc->collectable);
- if (0 > qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- if (GNUNET_DB_STATUS_HARD_ERROR == qs)
- *mret = TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "get_withdraw_info");
- return true; /* well, kind-of */
- }
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- return false;
- /* generate idempotent reply */
- TEH_METRICS_num_requests[TEH_MT_REQUEST_IDEMPOTENT_WITHDRAW]++;
- *mret = TALER_MHD_REPLY_JSON_PACK (
- rc->connection,
- MHD_HTTP_OK,
- TALER_JSON_pack_blinded_denom_sig ("ev_sig",
- &wc->collectable.sig));
- TALER_blinded_denom_sig_free (&wc->collectable.sig);
- return true;
-}
-
-
-MHD_RESULT
-TEH_handler_withdraw (struct TEH_RequestContext *rc,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- const json_t *root)
-{
- struct WithdrawContext wc;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("reserve_sig",
- &wc.collectable.reserve_sig),
- GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
- &wc.collectable.denom_pub_hash),
- TALER_JSON_spec_blinded_planchet ("coin_ev",
- &wc.blinded_planchet),
- GNUNET_JSON_spec_end ()
- };
- enum TALER_ErrorCode ec;
- struct TEH_DenominationKey *dk;
-
- memset (&wc,
- 0,
- sizeof (wc));
- wc.collectable.reserve_pub = *reserve_pub;
- {
- 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;
- }
- {
- MHD_RESULT mret;
- struct TEH_KeyStateHandle *ksh;
-
- ksh = TEH_keys_get_state ();
- if (NULL == ksh)
- {
- if (! check_request_idempotent (rc,
- &wc,
- &mret))
- {
- GNUNET_JSON_parse_free (spec);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
- NULL);
- }
- GNUNET_JSON_parse_free (spec);
- return mret;
- }
-
- dk = TEH_keys_denomination_by_hash_from_state (
- ksh,
- &wc.collectable.denom_pub_hash,
- NULL,
- NULL);
-
- if (NULL == dk)
- {
- if (! check_request_idempotent (rc,
- &wc,
- &mret))
- {
- GNUNET_JSON_parse_free (spec);
- return TEH_RESPONSE_reply_unknown_denom_pub_hash (
- rc->connection,
- &wc.collectable.denom_pub_hash);
- }
- GNUNET_JSON_parse_free (spec);
- return mret;
- }
- if (GNUNET_TIME_absolute_is_past (dk->meta.expire_withdraw.abs_time))
- {
- /* This denomination is past the expiration time for withdraws */
- if (! check_request_idempotent (rc,
- &wc,
- &mret))
- {
- GNUNET_JSON_parse_free (spec);
- return TEH_RESPONSE_reply_expired_denom_pub_hash (
- rc->connection,
- &wc.collectable.denom_pub_hash,
- TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
- "WITHDRAW");
- }
- GNUNET_JSON_parse_free (spec);
- return mret;
- }
- if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time))
- {
- /* This denomination is not yet valid, no need to check
- for idempotency! */
- GNUNET_JSON_parse_free (spec);
- return TEH_RESPONSE_reply_expired_denom_pub_hash (
- rc->connection,
- &wc.collectable.denom_pub_hash,
- TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
- "WITHDRAW");
- }
- if (dk->recoup_possible)
- {
- /* This denomination has been revoked */
- if (! check_request_idempotent (rc,
- &wc,
- &mret))
- {
- GNUNET_JSON_parse_free (spec);
- return TEH_RESPONSE_reply_expired_denom_pub_hash (
- rc->connection,
- &wc.collectable.denom_pub_hash,
- TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
- "WITHDRAW");
- }
- GNUNET_JSON_parse_free (spec);
- return mret;
- }
- if (dk->denom_pub.cipher != wc.blinded_planchet.cipher)
- {
- /* denomination cipher and blinded planchet cipher not the same */
- GNUNET_JSON_parse_free (spec);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH,
- NULL);
- }
- }
-
- if (0 >
- TALER_amount_add (&wc.collectable.amount_with_fee,
- &dk->meta.value,
- &dk->meta.fees.withdraw))
- {
- GNUNET_JSON_parse_free (spec);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_EXCHANGE_WITHDRAW_AMOUNT_FEE_OVERFLOW,
- NULL);
- }
-
- if (GNUNET_OK !=
- TALER_coin_ev_hash (&wc.blinded_planchet,
- &wc.collectable.denom_pub_hash,
- &wc.collectable.h_coin_envelope))
- {
- GNUNET_break (0);
- GNUNET_JSON_parse_free (spec);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- NULL);
- }
-
- TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
- if (GNUNET_OK !=
- TALER_wallet_withdraw_verify (&wc.collectable.denom_pub_hash,
- &wc.collectable.amount_with_fee,
- &wc.collectable.h_coin_envelope,
- &wc.collectable.reserve_pub,
- &wc.collectable.reserve_sig))
- {
- GNUNET_break_op (0);
- GNUNET_JSON_parse_free (spec);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_FORBIDDEN,
- TALER_EC_EXCHANGE_WITHDRAW_RESERVE_SIGNATURE_INVALID,
- NULL);
- }
-
- {
- struct TEH_CoinSignData csd = {
- .h_denom_pub = &wc.collectable.denom_pub_hash,
- .bp = &wc.blinded_planchet
- };
-
- /* Sign before transaction! */
- ec = TEH_keys_denomination_sign (
- &csd,
- false,
- &wc.collectable.sig);
- }
- if (TALER_EC_NONE != ec)
- {
- GNUNET_break (0);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to sign coin: %d\n",
- ec);
- GNUNET_JSON_parse_free (spec);
- return TALER_MHD_reply_with_ec (rc->connection,
- ec,
- NULL);
- }
-
- /* run transaction */
- {
- MHD_RESULT mhd_ret;
-
- if (GNUNET_OK !=
- TEH_DB_run_transaction (rc->connection,
- "run withdraw",
- TEH_MT_REQUEST_WITHDRAW,
- &mhd_ret,
- &withdraw_transaction,
- &wc))
- {
- /* Even if #withdraw_transaction() failed, it may have created a signature
- (or we might have done it optimistically above). */
- TALER_blinded_denom_sig_free (&wc.collectable.sig);
- GNUNET_JSON_parse_free (spec);
- return mhd_ret;
- }
- }
-
- /* Clean up and send back final response */
- GNUNET_JSON_parse_free (spec);
-
- if (! wc.kyc.ok)
- return TEH_RESPONSE_reply_kyc_required (rc->connection,
- &wc.h_account_payto,
- &wc.kyc);
-
- if (TALER_AML_NORMAL != wc.aml_decision)
- return TEH_RESPONSE_reply_aml_blocked (rc->connection,
- wc.aml_decision);
-
- {
- MHD_RESULT ret;
-
- ret = TALER_MHD_REPLY_JSON_PACK (
- rc->connection,
- MHD_HTTP_OK,
- TALER_JSON_pack_blinded_denom_sig ("ev_sig",
- &wc.collectable.sig));
- TALER_blinded_denom_sig_free (&wc.collectable.sig);
- return ret;
- }
-}
-
-
-/* end of taler-exchange-httpd_withdraw.c */
diff --git a/src/exchange/taler-exchange-httpd_withdraw.h b/src/exchange/taler-exchange-httpd_withdraw.h
deleted file mode 100644
index 2ec76bb92..000000000
--- a/src/exchange/taler-exchange-httpd_withdraw.h
+++ /dev/null
@@ -1,47 +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_withdraw.h
- * @brief Handle /reserve/withdraw requests
- * @author Florian Dold
- * @author Benedikt Mueller
- * @author Christian Grothoff
- */
-#ifndef TALER_EXCHANGE_HTTPD_WITHDRAW_H
-#define TALER_EXCHANGE_HTTPD_WITHDRAW_H
-
-#include <microhttpd.h>
-#include "taler-exchange-httpd.h"
-
-
-/**
- * Handle a "/reserves/$RESERVE_PUB/withdraw" request. Parses the requested "denom_pub" which
- * specifies the key/value of the coin to be withdrawn, and checks that the
- * signature "reserve_sig" makes this a valid withdrawal request from the
- * specified reserve. If so, the envelope with the blinded coin "coin_ev" is
- * 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_withdraw (struct TEH_RequestContext *rc,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- const json_t *root);
-
-#endif