summaryrefslogtreecommitdiff
path: root/src/exchange/taler-exchange-httpd_withdraw.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/exchange/taler-exchange-httpd_withdraw.c')
-rw-r--r--src/exchange/taler-exchange-httpd_withdraw.c222
1 files changed, 127 insertions, 95 deletions
diff --git a/src/exchange/taler-exchange-httpd_withdraw.c b/src/exchange/taler-exchange-httpd_withdraw.c
index 0a334ec4c..4575a8ab5 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;
+ }
}