simplify refund processing, add additional checks for matching currency
This commit is contained in:
parent
c04bcb0a82
commit
cd83daaeae
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user