simplify refund processing, add additional checks for matching currency

This commit is contained in:
Christian Grothoff 2020-03-16 20:22:30 +01:00
parent c04bcb0a82
commit cd83daaeae
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
4 changed files with 281 additions and 238 deletions

View File

@ -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,

View File

@ -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,
@ -81,51 +82,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
@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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
@ -528,6 +528,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
@ -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
@ -607,6 +614,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