summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2020-03-16 20:22:30 +0100
committerChristian Grothoff <christian@grothoff.org>2020-03-16 20:23:45 +0100
commitcd83daaeae915e0c1b6170cb11f40aa1cfbfece4 (patch)
tree72852006cbe2cf1aad854ced1c773384a95462b6
parentc04bcb0a828e739f0b23a98748d81a9f0993cc06 (diff)
downloadexchange-cd83daaeae915e0c1b6170cb11f40aa1cfbfece4.tar.gz
exchange-cd83daaeae915e0c1b6170cb11f40aa1cfbfece4.tar.bz2
exchange-cd83daaeae915e0c1b6170cb11f40aa1cfbfece4.zip
simplify refund processing, add additional checks for matching currency
-rw-r--r--src/exchange/taler-exchange-httpd_deposit.c14
-rw-r--r--src/exchange/taler-exchange-httpd_refund.c259
-rw-r--r--src/exchange/taler-exchange-httpd_withdraw.c222
-rw-r--r--src/include/taler_error_codes.h24
4 files changed, 281 insertions, 238 deletions
diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c
index 4008ed27..039ca9f9 100644
--- a/src/exchange/taler-exchange-httpd_deposit.c
+++ b/src/exchange/taler-exchange-httpd_deposit.c
@@ -417,8 +417,8 @@ TEH_handler_deposit (struct MHD_Connection *connection,
&hc);
if (NULL == dki)
{
- TEH_KS_release (key_state);
TALER_LOG_WARNING ("Unknown denomination key in /deposit request\n");
+ TEH_KS_release (key_state);
GNUNET_JSON_parse_free (spec);
return TALER_MHD_reply_with_error (connection,
hc,
@@ -427,6 +427,18 @@ TEH_handler_deposit (struct MHD_Connection *connection,
}
TALER_amount_ntoh (&deposit.deposit_fee,
&dki->issue.properties.fee_deposit);
+ if (GNUNET_YES !=
+ TALER_amount_cmp_currency (&deposit.amount_with_fee,
+ &deposit.deposit_fee) )
+ {
+ GNUNET_break_op (0);
+ TEH_KS_release (key_state);
+ GNUNET_JSON_parse_free (spec);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_DEPOSIT_CURRENCY_MISSMATCH,
+ "contribution");
+ }
/* check coin signature */
if (GNUNET_YES !=
TALER_test_coin_valid (&deposit.coin,
diff --git a/src/exchange/taler-exchange-httpd_refund.c b/src/exchange/taler-exchange-httpd_refund.c
index d564a361..e590703a 100644
--- a/src/exchange/taler-exchange-httpd_refund.c
+++ b/src/exchange/taler-exchange-httpd_refund.c
@@ -15,7 +15,7 @@
*/
/**
* @file taler-exchange-httpd_refund.c
- * @brief Handle /refund requests; parses the POST and JSON and
+ * @brief Handle refund requests; parses the POST and JSON and
* verifies the coin signature before handing things off
* to the database.
* @author Florian Dold
@@ -48,16 +48,17 @@ reply_refund_success (struct MHD_Connection *connection,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_EXCHANGEDB_RefundListEntry *refund)
{
- struct TALER_RefundConfirmationPS rc;
struct TALER_ExchangePublicKeyP pub;
struct TALER_ExchangeSignatureP sig;
+ struct TALER_RefundConfirmationPS rc = {
+ .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND),
+ .purpose.size = htonl (sizeof (rc)),
+ .h_contract_terms = refund->h_contract_terms,
+ .coin_pub = *coin_pub,
+ .merchant = refund->merchant_pub,
+ .rtransaction_id = GNUNET_htonll (refund->rtransaction_id)
+ };
- rc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND);
- rc.purpose.size = htonl (sizeof (struct TALER_RefundConfirmationPS));
- rc.h_contract_terms = refund->h_contract_terms;
- rc.coin_pub = *coin_pub;
- rc.merchant = refund->merchant_pub;
- rc.rtransaction_id = GNUNET_htonll (refund->rtransaction_id);
TALER_amount_hton (&rc.refund_amount,
&refund->refund_amount);
TALER_amount_hton (&rc.refund_fee,
@@ -70,7 +71,7 @@ reply_refund_success (struct MHD_Connection *connection,
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_EXCHANGE_BAD_CONFIGURATION,
- "no keys");
+ "no online signing key");
}
return TALER_MHD_reply_json_pack (connection,
MHD_HTTP_OK,
@@ -82,51 +83,6 @@ reply_refund_success (struct MHD_Connection *connection,
/**
- * Generate refund conflict failure message. Returns the
- * transaction list @a tl with the details about the conflict.
- *
- * @param connection connection to the client
- * @param coin_pub public key this is about
- * @param tl transaction list showing the conflict
- * @return MHD result code
- */
-static int
-reply_refund_conflict (struct MHD_Connection *connection,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
- const struct TALER_EXCHANGEDB_TransactionList *tl)
-{
- return TALER_MHD_reply_json_pack (connection,
- MHD_HTTP_CONFLICT,
- "{s:s, s:I, s:o}",
- "hint", "conflicting refund",
- "code",
- (json_int_t) TALER_EC_REFUND_CONFLICT,
- "history",
- TEH_RESPONSE_compile_transaction_history (
- coin_pub,
- tl));
-}
-
-
-/**
- * Closure for the transaction.
- */
-struct TALER_EXCHANGEDB_RefundContext
-{
- /**
- * Information about the refund.
- */
- const struct TALER_EXCHANGEDB_Refund *refund;
-
- /**
- * Expected refund fee by the denomination of the coin.
- */
- struct TALER_Amount expect_fee;
-
-};
-
-
-/**
* Execute a "/refund" transaction. Returns a confirmation that the
* refund was successful, or a failure if we are not aware of a
* matching /deposit or if it is too late to do the refund.
@@ -150,18 +106,14 @@ refund_transaction (void *cls,
struct TALER_EXCHANGEDB_Session *session,
int *mhd_ret)
{
- struct TALER_EXCHANGEDB_RefundContext *rc = cls;
- const struct TALER_EXCHANGEDB_Refund *refund = rc->refund;
+ const struct TALER_EXCHANGEDB_Refund *refund = cls;
struct TALER_EXCHANGEDB_TransactionList *tl;
const struct TALER_EXCHANGEDB_DepositListEntry *dep;
const struct TALER_EXCHANGEDB_RefundListEntry *ref;
enum GNUNET_DB_QueryStatus qs;
int deposit_found;
int refund_found;
- int fee_cmp;
- dep = NULL;
- ref = NULL;
tl = NULL;
qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
session,
@@ -177,6 +129,8 @@ refund_transaction (void *cls,
"database transaction failure");
return qs;
}
+ dep = NULL;
+ ref = NULL;
deposit_found = GNUNET_NO;
refund_found = GNUNET_NO;
for (struct TALER_EXCHANGEDB_TransactionList *tlp = tl;
@@ -267,9 +221,15 @@ refund_transaction (void *cls,
/* handle if conflicting refund found */
if (GNUNET_SYSERR == refund_found)
{
- *mhd_ret = reply_refund_conflict (connection,
- &refund->coin.coin_pub,
- tl);
+ *mhd_ret = TALER_MHD_reply_json_pack (
+ connection,
+ MHD_HTTP_CONFLICT,
+ "{s:s, s:I, s:o}",
+ "hint", "conflicting refund",
+ "code", (json_int_t) TALER_EC_REFUND_CONFLICT,
+ "history", TEH_RESPONSE_compile_transaction_history (
+ &refund->coin.coin_pub,
+ tl));
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl);
return GNUNET_DB_STATUS_HARD_ERROR;
@@ -321,7 +281,7 @@ refund_transaction (void *cls,
*mhd_ret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_REFUND_DB_INCONSISTENT,
- "database inconsistent");
+ "database inconsistent (deposit data became inaccessible during transaction)");
return qs;
}
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
@@ -352,24 +312,6 @@ refund_transaction (void *cls,
"refund requested exceeds original value");
return GNUNET_DB_STATUS_HARD_ERROR;
}
- /* Check refund fee matches fee of denomination key! */
- fee_cmp = TALER_amount_cmp (&refund->details.refund_fee,
- &rc->expect_fee);
- if (-1 == fee_cmp)
- {
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_REFUND_FEE_TOO_LOW,
- "refund_fee");
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- if (1 == fee_cmp)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Refund fee proposed by merchant is higher than necessary.\n");
- }
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl);
@@ -405,50 +347,35 @@ static int
verify_and_execute_refund (struct MHD_Connection *connection,
const struct TALER_EXCHANGEDB_Refund *refund)
{
- struct TALER_EXCHANGEDB_RefundContext rc;
- struct TALER_RefundRequestPS rr;
struct GNUNET_HashCode denom_hash;
+ struct TALER_Amount expect_fee;
- if (GNUNET_YES !=
- TALER_amount_cmp_currency (&refund->details.refund_amount,
- &refund->details.refund_fee) )
{
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_REFUND_FEE_CURRENCY_MISSMATCH,
- "refund_fee");
- }
- if (-1 == TALER_amount_cmp (&refund->details.refund_amount,
- &refund->details.refund_fee) )
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_REFUND_FEE_ABOVE_AMOUNT,
- "refund_amount");
- }
- rr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND);
- rr.purpose.size = htonl (sizeof (struct TALER_RefundRequestPS));
- rr.h_contract_terms = refund->details.h_contract_terms;
- rr.coin_pub = refund->coin.coin_pub;
- rr.merchant = refund->details.merchant_pub;
- rr.rtransaction_id = GNUNET_htonll (refund->details.rtransaction_id);
- TALER_amount_hton (&rr.refund_amount,
- &refund->details.refund_amount);
- TALER_amount_hton (&rr.refund_fee,
- &refund->details.refund_fee);
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
- &rr.purpose,
- &refund->details.merchant_sig.eddsa_sig,
- &refund->details.merchant_pub.eddsa_pub))
- {
- TALER_LOG_WARNING ("Invalid signature on /refund request\n");
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_FORBIDDEN,
- TALER_EC_REFUND_MERCHANT_SIGNATURE_INVALID,
- "merchant_sig");
+ struct TALER_RefundRequestPS rr = {
+ .purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND),
+ .purpose.size = htonl (sizeof (rr)),
+ .h_contract_terms = refund->details.h_contract_terms,
+ .coin_pub = refund->coin.coin_pub,
+ .merchant = refund->details.merchant_pub,
+ .rtransaction_id = GNUNET_htonll (refund->details.rtransaction_id)
+ };
+
+ TALER_amount_hton (&rr.refund_amount,
+ &refund->details.refund_amount);
+ TALER_amount_hton (&rr.refund_fee,
+ &refund->details.refund_fee);
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
+ &rr.purpose,
+ &refund->details.merchant_sig.eddsa_sig,
+ &refund->details.merchant_pub.eddsa_pub))
+ {
+ TALER_LOG_WARNING ("Invalid signature on refund request\n");
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_FORBIDDEN,
+ TALER_EC_REFUND_MERCHANT_SIGNATURE_INVALID,
+ "merchant_sig");
+ }
}
/* Fetch the coin's denomination (hash) */
@@ -503,23 +430,53 @@ verify_and_execute_refund (struct MHD_Connection *connection,
ec,
"denomination not found, but coin known");
}
- TALER_amount_ntoh (&rc.expect_fee,
+ TALER_amount_ntoh (&expect_fee,
&dki->issue.properties.fee_refund);
}
TEH_KS_release (key_state);
}
+ /* Check refund fee matches fee of denomination key! */
+ if (GNUNET_YES !=
+ TALER_amount_cmp_currency (&expect_fee,
+ &refund->details.refund_fee) )
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_REFUND_FEE_CURRENCY_MISSMATCH,
+ "refund_fee");
+ }
+ {
+ int fee_cmp;
+
+ fee_cmp = TALER_amount_cmp (&refund->details.refund_fee,
+ &expect_fee);
+ if (-1 == fee_cmp)
+ {
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_REFUND_FEE_TOO_LOW,
+ "refund_fee");
+ }
+ if (1 == fee_cmp)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Refund fee proposed by merchant is higher than necessary.\n");
+ }
+ }
+
+
/* Finally run the actual transaction logic */
{
int mhd_ret;
- rc.refund = refund;
if (GNUNET_OK !=
TEH_DB_run_transaction (connection,
"run refund",
&mhd_ret,
&refund_transaction,
- &rc))
+ (void *) refund))
{
return mhd_ret;
}
@@ -546,7 +503,6 @@ TEH_handler_refund (struct MHD_Connection *connection,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const json_t *root)
{
- int res;
struct TALER_EXCHANGEDB_Refund refund;
struct GNUNET_JSON_Specification spec[] = {
TALER_JSON_spec_amount ("refund_amount", &refund.details.refund_amount),
@@ -561,17 +517,46 @@ TEH_handler_refund (struct MHD_Connection *connection,
};
refund.coin.coin_pub = *coin_pub;
- res = TALER_MHD_parse_json_data (connection,
- root,
- spec);
- if (GNUNET_SYSERR == res)
- return MHD_NO; /* hard failure */
- if (GNUNET_NO == res)
- return MHD_YES; /* failure */
- res = verify_and_execute_refund (connection,
- &refund);
- GNUNET_JSON_parse_free (spec);
- return res;
+ {
+ int res;
+
+ res = TALER_MHD_parse_json_data (connection,
+ root,
+ spec);
+ if (GNUNET_SYSERR == res)
+ return MHD_NO; /* hard failure */
+ if (GNUNET_NO == res)
+ return MHD_YES; /* failure */
+ }
+ if (GNUNET_YES !=
+ TALER_amount_cmp_currency (&refund.details.refund_amount,
+ &refund.details.refund_fee) )
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_REFUND_FEE_CURRENCY_MISSMATCH,
+ "refund_amount or refund_fee");
+ }
+ if (-1 == TALER_amount_cmp (&refund.details.refund_amount,
+ &refund.details.refund_fee) )
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_REFUND_FEE_ABOVE_AMOUNT,
+ "refund_amount");
+ }
+ {
+ int res;
+
+ res = verify_and_execute_refund (connection,
+ &refund);
+ GNUNET_JSON_parse_free (spec);
+ return res;
+ }
}
diff --git a/src/exchange/taler-exchange-httpd_withdraw.c b/src/exchange/taler-exchange-httpd_withdraw.c
index 0a334ec4..4575a8ab 100644
--- a/src/exchange/taler-exchange-httpd_withdraw.c
+++ b/src/exchange/taler-exchange-httpd_withdraw.c
@@ -18,7 +18,7 @@
*/
/**
* @file taler-exchange-httpd_withdraw.c
- * @brief Handle /reserve/withdraw requests
+ * @brief Handle /reserves/$RESERVE_PUB/withdraw requests
* @author Florian Dold
* @author Benedikt Mueller
* @author Christian Grothoff
@@ -42,7 +42,7 @@
/**
- * Send reserve status information to client with the
+ * Send reserve history information to client with the
* message that we have insufficient funds for the
* requested withdraw operation.
*
@@ -52,10 +52,10 @@
* @return MHD result code
*/
static int
-reply_reserve_withdraw_insufficient_funds (struct MHD_Connection *connection,
- const struct TALER_Amount *ebalance,
- const struct
- TALER_EXCHANGEDB_ReserveHistory *rh)
+reply_withdraw_insufficient_funds (
+ struct MHD_Connection *connection,
+ const struct TALER_Amount *ebalance,
+ const struct TALER_EXCHANGEDB_ReserveHistory *rh)
{
json_t *json_history;
struct TALER_Amount balance;
@@ -66,7 +66,7 @@ reply_reserve_withdraw_insufficient_funds (struct MHD_Connection *connection,
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_WITHDRAW_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS,
- "balance calculation failure");
+ "reserve balance calculation failure");
if (0 !=
TALER_amount_cmp (&balance,
ebalance))
@@ -75,7 +75,7 @@ reply_reserve_withdraw_insufficient_funds (struct MHD_Connection *connection,
json_decref (json_history);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_WITHDRAW_HISTORY_RESERVE_BALANCE_CORRUPT,
+ TALER_EC_WITHDRAW_RESERVE_BALANCE_CORRUPT,
"internal balance inconsistency error");
}
return TALER_MHD_reply_json_pack (connection,
@@ -92,21 +92,25 @@ reply_reserve_withdraw_insufficient_funds (struct MHD_Connection *connection,
/**
- * Send blinded coin information to client.
+ * Send blind signature to client.
*
* @param connection connection to the client
* @param collectable blinded coin to return
* @return MHD result code
*/
static int
-reply_reserve_withdraw_success (struct MHD_Connection *connection,
- const struct
- TALER_EXCHANGEDB_CollectableBlindcoin *
- collectable)
+reply_withdraw_success (
+ struct MHD_Connection *connection,
+ const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable)
{
json_t *sig_json;
sig_json = GNUNET_JSON_from_rsa_signature (collectable->sig.rsa_signature);
+ if (NULL == sig_json)
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_JSON_ALLOCATION_FAILURE,
+ "GNUNET_JSON_from_rsa_signature() failed");
return TALER_MHD_reply_json_pack (connection,
MHD_HTTP_OK,
"{s:o}",
@@ -168,7 +172,7 @@ struct WithdrawContext
/**
- * Function implementing /reserve/withdraw transaction. Runs the
+ * 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.
@@ -180,9 +184,9 @@ struct WithdrawContext
* before entering the transaction, or because this function is run
* twice (!) by #TEH_DB_run_transaction() and the first time created
* the signature and then failed to commit. Furthermore, we may get
- * a 2nd correct signature briefly if "get_withdraw_info" suceeds and
+ * a 2nd correct signature briefly if "get_withdraw_info" succeeds and
* finds one in the DB. To avoid signing twice, the function may
- * return a valid signature in "wc->collectable.sig" even if it failed.
+ * return a valid signature in "wc->collectable.sig" **even if it failed**.
* The caller must thus free the signature in either case.
*
* @param cls a `struct WithdrawContext *`
@@ -201,7 +205,6 @@ withdraw_transaction (void *cls,
struct WithdrawContext *wc = cls;
struct TALER_EXCHANGEDB_Reserve r;
enum GNUNET_DB_QueryStatus qs;
- struct TALER_Amount fee_withdraw;
struct TALER_DenominationSignature denom_sig;
#if OPTIMISTIC_SIGN
@@ -227,18 +230,23 @@ withdraw_transaction (void *cls,
}
/* Don't sign again if we have already signed the coin */
- if (1 == qs)
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
{
+ /* Toss out the optimistic signature, we got another one from the DB;
+ optimization trade-off loses in this case: we unnecessarily computed
+ a signature :-( */
#if OPTIMISTIC_SIGN
GNUNET_CRYPTO_rsa_signature_free (denom_sig.rsa_signature);
#endif
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
- GNUNET_assert (0 == qs);
- wc->collectable.sig = denom_sig;
+ /* We should never get more than one result, and we handled
+ the errors (negative case) above, so that leaves no results. */
+ GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs);
+ wc->collectable.sig = denom_sig; /* Note: might still be NULL if we didn't do OPTIMISTIC_SIGN */
/* Check if balance is sufficient */
- r.pub = wc->wsrd.reserve_pub;
+ r.pub = wc->wsrd.reserve_pub; /* other fields of 'r' initialized in reserves_get (if successful) */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Trying to withdraw from reserve: %s\n",
TALER_B2S (&r.pub));
@@ -265,20 +273,25 @@ withdraw_transaction (void *cls,
if (0 < TALER_amount_cmp (&wc->amount_required,
&r.balance))
{
- char *amount_required;
- char *r_balance;
struct TALER_EXCHANGEDB_ReserveHistory *rh;
/* The reserve does not have the required amount (actual
* amount + withdraw fee) */
GNUNET_break_op (0);
- amount_required = TALER_amount_to_string (&wc->amount_required);
- r_balance = TALER_amount_to_string (&r.balance);
- TALER_LOG_WARNING ("Asked %s over a reserve worth %s\n",
- amount_required,
- r_balance);
- GNUNET_free (amount_required);
- GNUNET_free (r_balance);
+#if GNUNET_EXTRA_LOGGING
+ {
+ char *amount_required;
+ char *r_balance;
+
+ amount_required = TALER_amount_to_string (&wc->amount_required);
+ r_balance = TALER_amount_to_string (&r.balance);
+ TALER_LOG_WARNING ("Asked %s over a reserve worth %s\n",
+ amount_required,
+ r_balance);
+ GNUNET_free (amount_required);
+ GNUNET_free (r_balance);
+ }
+#endif
qs = TEH_plugin->get_reserve_history (TEH_plugin->cls,
session,
&wc->wsrd.reserve_pub,
@@ -292,9 +305,9 @@ withdraw_transaction (void *cls,
"failed to fetch reserve history");
return GNUNET_DB_STATUS_HARD_ERROR;
}
- *mhd_ret = reply_reserve_withdraw_insufficient_funds (connection,
- &r.balance,
- rh);
+ *mhd_ret = reply_withdraw_insufficient_funds (connection,
+ &r.balance,
+ rh);
TEH_plugin->free_reserve_history (TEH_plugin->cls,
rh);
return GNUNET_DB_STATUS_HARD_ERROR;
@@ -314,16 +327,15 @@ withdraw_transaction (void *cls,
*mhd_ret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_WITHDRAW_SIGNATURE_FAILED,
- "Failed to create signature");
+ "Failed to create blind signature");
return GNUNET_DB_STATUS_HARD_ERROR;
}
}
#endif
- TALER_amount_ntoh (&fee_withdraw,
- &wc->dki->issue.properties.fee_withdraw);
wc->collectable.denom_pub_hash = wc->denom_pub_hash;
wc->collectable.amount_with_fee = wc->amount_required;
- wc->collectable.withdraw_fee = fee_withdraw;
+ TALER_amount_ntoh (&wc->collectable.withdraw_fee,
+ &wc->dki->issue.properties.fee_withdraw);
wc->collectable.reserve_pub = wc->wsrd.reserve_pub;
wc->collectable.h_coin_envelope = wc->wsrd.h_coin_envelope;
wc->collectable.reserve_sig = wc->signature;
@@ -366,12 +378,6 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh,
const char *const args[2])
{
struct WithdrawContext wc;
- int res;
- int mhd_ret;
- unsigned int hc;
- enum TALER_ErrorCode ec;
- struct TALER_Amount amount;
- struct TALER_Amount fee_withdraw;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_varsize ("coin_ev",
(void **) &wc.blinded_msg,
@@ -397,11 +403,15 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh,
"reserve public key malformed");
}
- res = TALER_MHD_parse_json_data (connection,
- root,
- spec);
- if (GNUNET_OK != res)
- return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ {
+ int res;
+
+ res = TALER_MHD_parse_json_data (connection,
+ root,
+ spec);
+ if (GNUNET_OK != res)
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
wc.key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ());
if (NULL == wc.key_state)
{
@@ -412,41 +422,52 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh,
TALER_EC_EXCHANGE_BAD_CONFIGURATION,
"no keys");
}
- wc.dki = TEH_KS_denomination_key_lookup_by_hash (wc.key_state,
- &wc.denom_pub_hash,
- TEH_KS_DKU_WITHDRAW,
- &ec,
- &hc);
- if (NULL == wc.dki)
{
- GNUNET_JSON_parse_free (spec);
- TEH_KS_release (wc.key_state);
- return TALER_MHD_reply_with_error (connection,
- hc,
- ec,
- "could not find denomination key");
+ unsigned int hc;
+ enum TALER_ErrorCode ec;
+
+ wc.dki = TEH_KS_denomination_key_lookup_by_hash (wc.key_state,
+ &wc.denom_pub_hash,
+ TEH_KS_DKU_WITHDRAW,
+ &ec,
+ &hc);
+ if (NULL == wc.dki)
+ {
+ GNUNET_JSON_parse_free (spec);
+ TEH_KS_release (wc.key_state);
+ return TALER_MHD_reply_with_error (connection,
+ hc,
+ ec,
+ "could not find denomination key");
+ }
}
GNUNET_assert (NULL != wc.dki->denom_priv.rsa_private_key);
- TALER_amount_ntoh (&amount,
- &wc.dki->issue.properties.value);
- TALER_amount_ntoh (&fee_withdraw,
- &wc.dki->issue.properties.fee_withdraw);
- if (GNUNET_OK !=
- TALER_amount_add (&wc.amount_required,
- &amount,
- &fee_withdraw))
{
- GNUNET_JSON_parse_free (spec);
- TEH_KS_release (wc.key_state);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_WITHDRAW_AMOUNT_FEE_OVERFLOW,
- "amount overflow for value plus withdraw fee");
+ struct TALER_Amount amount;
+ struct TALER_Amount fee_withdraw;
+
+ TALER_amount_ntoh (&amount,
+ &wc.dki->issue.properties.value);
+ TALER_amount_ntoh (&fee_withdraw,
+ &wc.dki->issue.properties.fee_withdraw);
+ if (GNUNET_OK !=
+ TALER_amount_add (&wc.amount_required,
+ &amount,
+ &fee_withdraw))
+ {
+ GNUNET_JSON_parse_free (spec);
+ TEH_KS_release (wc.key_state);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_WITHDRAW_AMOUNT_FEE_OVERFLOW,
+ "amount overflow for value plus withdraw fee");
+ }
+ TALER_amount_hton (&wc.wsrd.amount_with_fee,
+ &wc.amount_required);
+ TALER_amount_hton (&wc.wsrd.withdraw_fee,
+ &fee_withdraw);
}
- TALER_amount_hton (&wc.wsrd.amount_with_fee,
- &wc.amount_required);
- TALER_amount_hton (&wc.wsrd.withdraw_fee,
- &fee_withdraw);
+
/* verify signature! */
wc.wsrd.purpose.size
= htonl (sizeof (struct TALER_WithdrawRequestPS));
@@ -491,28 +512,39 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh,
}
#endif
- if (GNUNET_OK !=
- TEH_DB_run_transaction (connection,
- "run withdraw",
- &mhd_ret,
- &withdraw_transaction,
- &wc))
+ /* run transaction and sign (if not optimistically signed before) */
{
- TEH_KS_release (wc.key_state);
- /* Even if #withdraw_transaction() failed, it may have created a signature
- (or we might have done it optimistically above). */
- if (NULL != wc.collectable.sig.rsa_signature)
- GNUNET_CRYPTO_rsa_signature_free (wc.collectable.sig.rsa_signature);
- GNUNET_JSON_parse_free (spec);
- return mhd_ret;
+ int mhd_ret;
+
+ if (GNUNET_OK !=
+ TEH_DB_run_transaction (connection,
+ "run withdraw",
+ &mhd_ret,
+ &withdraw_transaction,
+ &wc))
+ {
+ TEH_KS_release (wc.key_state);
+ /* Even if #withdraw_transaction() failed, it may have created a signature
+ (or we might have done it optimistically above). */
+ if (NULL != wc.collectable.sig.rsa_signature)
+ GNUNET_CRYPTO_rsa_signature_free (wc.collectable.sig.rsa_signature);
+ GNUNET_JSON_parse_free (spec);
+ return mhd_ret;
+ }
}
+
+ /* Clean up and send back final (positive) response */
TEH_KS_release (wc.key_state);
GNUNET_JSON_parse_free (spec);
- mhd_ret = reply_reserve_withdraw_success (connection,
- &wc.collectable);
- GNUNET_CRYPTO_rsa_signature_free (wc.collectable.sig.rsa_signature);
- return mhd_ret;
+ {
+ int ret;
+
+ ret = reply_withdraw_success (connection,
+ &wc.collectable);
+ GNUNET_CRYPTO_rsa_signature_free (wc.collectable.sig.rsa_signature);
+ return ret;
+ }
}
diff --git a/src/include/taler_error_codes.h b/src/include/taler_error_codes.h
index e6bd7364..9a7c544b 100644
--- a/src/include/taler_error_codes.h
+++ b/src/include/taler_error_codes.h
@@ -382,11 +382,11 @@ enum TALER_ErrorCode
TALER_EC_DENOMINATION_KEY_LOST = 1116,
/**
- * The exchange's database entry with the reserve balance summary
- * is inconsistent with its own history of the reserve.
- * Returned with an HTTP status of #MHD_HTTP_INTERNAL_SERVER_ERROR.
+ * The exchange's database entry with the reserve balance summary is
+ * inconsistent with its own history of the reserve. Returned with an
+ * HTTP status of #MHD_HTTP_INTERNAL_SERVER_ERROR.
*/
- TALER_EC_WITHDRAW_HISTORY_RESERVE_BALANCE_CORRUPT = 1117,
+ TALER_EC_WITHDRAW_RESERVE_BALANCE_CORRUPT = 1117,
/**
* The exchange failed to obtain the transaction history of the given
@@ -529,6 +529,13 @@ enum TALER_ErrorCode
TALER_EC_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE = 1221,
/**
+ * The currency specified for the deposit is different from the
+ * currency of the coin. This response is provided with HTTP status
+ * code MHD_HTTP_PRECONDITION_FAILED.
+ */
+ TALER_EC_DEPOSIT_CURRENCY_MISSMATCH = 1222,
+
+ /**
* The respective coin did not have sufficient residual value for the
* /refresh/melt operation. The "history" in this response provdes
* the "residual_value" of the coin, which may be less than its
@@ -544,7 +551,7 @@ enum TALER_ErrorCode
* "original_value". This response is provided with HTTP status code
* MHD_HTTP_CONFLICT.
*/
- TALER_EC_TALER_EC_REFRESH_MELT_DENOMINATION_KEY_NOT_FOUND = 1301,
+ TALER_EC_REFRESH_MELT_DENOMINATION_KEY_NOT_FOUND = 1301,
/**
* The exchange had an internal error reconstructing the transaction
@@ -608,6 +615,13 @@ enum TALER_ErrorCode
TALER_EC_MELT_INVALID_SIGNATURE_BY_EXCHANGE = 1310,
/**
+ * The currency specified for the melt amount is different from the
+ * currency of the coin. This response is provided with HTTP status
+ * code MHD_HTTP_PRECONDITION_FAILED.
+ */
+ TALER_EC_MELT_CURRENCY_MISSMATCH = 1311,
+
+ /**
* The exchange is unaware of the denomination key that was used to
* sign the melted zombie coin. This response is provided with HTTP
* status code MHD_HTTP_NOT_FOUND.