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);
|
&hc);
|
||||||
if (NULL == dki)
|
if (NULL == dki)
|
||||||
{
|
{
|
||||||
TEH_KS_release (key_state);
|
|
||||||
TALER_LOG_WARNING ("Unknown denomination key in /deposit request\n");
|
TALER_LOG_WARNING ("Unknown denomination key in /deposit request\n");
|
||||||
|
TEH_KS_release (key_state);
|
||||||
GNUNET_JSON_parse_free (spec);
|
GNUNET_JSON_parse_free (spec);
|
||||||
return TALER_MHD_reply_with_error (connection,
|
return TALER_MHD_reply_with_error (connection,
|
||||||
hc,
|
hc,
|
||||||
@ -427,6 +427,18 @@ TEH_handler_deposit (struct MHD_Connection *connection,
|
|||||||
}
|
}
|
||||||
TALER_amount_ntoh (&deposit.deposit_fee,
|
TALER_amount_ntoh (&deposit.deposit_fee,
|
||||||
&dki->issue.properties.fee_deposit);
|
&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 */
|
/* check coin signature */
|
||||||
if (GNUNET_YES !=
|
if (GNUNET_YES !=
|
||||||
TALER_test_coin_valid (&deposit.coin,
|
TALER_test_coin_valid (&deposit.coin,
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* @file taler-exchange-httpd_refund.c
|
* @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
|
* verifies the coin signature before handing things off
|
||||||
* to the database.
|
* to the database.
|
||||||
* @author Florian Dold
|
* @author Florian Dold
|
||||||
@ -48,16 +48,17 @@ reply_refund_success (struct MHD_Connection *connection,
|
|||||||
const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
||||||
const struct TALER_EXCHANGEDB_RefundListEntry *refund)
|
const struct TALER_EXCHANGEDB_RefundListEntry *refund)
|
||||||
{
|
{
|
||||||
struct TALER_RefundConfirmationPS rc;
|
|
||||||
struct TALER_ExchangePublicKeyP pub;
|
struct TALER_ExchangePublicKeyP pub;
|
||||||
struct TALER_ExchangeSignatureP sig;
|
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,
|
TALER_amount_hton (&rc.refund_amount,
|
||||||
&refund->refund_amount);
|
&refund->refund_amount);
|
||||||
TALER_amount_hton (&rc.refund_fee,
|
TALER_amount_hton (&rc.refund_fee,
|
||||||
@ -70,7 +71,7 @@ reply_refund_success (struct MHD_Connection *connection,
|
|||||||
return TALER_MHD_reply_with_error (connection,
|
return TALER_MHD_reply_with_error (connection,
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
TALER_EC_EXCHANGE_BAD_CONFIGURATION,
|
TALER_EC_EXCHANGE_BAD_CONFIGURATION,
|
||||||
"no keys");
|
"no online signing key");
|
||||||
}
|
}
|
||||||
return TALER_MHD_reply_json_pack (connection,
|
return TALER_MHD_reply_json_pack (connection,
|
||||||
MHD_HTTP_OK,
|
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
|
* Execute a "/refund" transaction. Returns a confirmation that the
|
||||||
* refund was successful, or a failure if we are not aware of a
|
* 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,
|
struct TALER_EXCHANGEDB_Session *session,
|
||||||
int *mhd_ret)
|
int *mhd_ret)
|
||||||
{
|
{
|
||||||
struct TALER_EXCHANGEDB_RefundContext *rc = cls;
|
const struct TALER_EXCHANGEDB_Refund *refund = cls;
|
||||||
const struct TALER_EXCHANGEDB_Refund *refund = rc->refund;
|
|
||||||
struct TALER_EXCHANGEDB_TransactionList *tl;
|
struct TALER_EXCHANGEDB_TransactionList *tl;
|
||||||
const struct TALER_EXCHANGEDB_DepositListEntry *dep;
|
const struct TALER_EXCHANGEDB_DepositListEntry *dep;
|
||||||
const struct TALER_EXCHANGEDB_RefundListEntry *ref;
|
const struct TALER_EXCHANGEDB_RefundListEntry *ref;
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
int deposit_found;
|
int deposit_found;
|
||||||
int refund_found;
|
int refund_found;
|
||||||
int fee_cmp;
|
|
||||||
|
|
||||||
dep = NULL;
|
|
||||||
ref = NULL;
|
|
||||||
tl = NULL;
|
tl = NULL;
|
||||||
qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
|
qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
|
||||||
session,
|
session,
|
||||||
@ -177,6 +129,8 @@ refund_transaction (void *cls,
|
|||||||
"database transaction failure");
|
"database transaction failure");
|
||||||
return qs;
|
return qs;
|
||||||
}
|
}
|
||||||
|
dep = NULL;
|
||||||
|
ref = NULL;
|
||||||
deposit_found = GNUNET_NO;
|
deposit_found = GNUNET_NO;
|
||||||
refund_found = GNUNET_NO;
|
refund_found = GNUNET_NO;
|
||||||
for (struct TALER_EXCHANGEDB_TransactionList *tlp = tl;
|
for (struct TALER_EXCHANGEDB_TransactionList *tlp = tl;
|
||||||
@ -267,9 +221,15 @@ refund_transaction (void *cls,
|
|||||||
/* handle if conflicting refund found */
|
/* handle if conflicting refund found */
|
||||||
if (GNUNET_SYSERR == refund_found)
|
if (GNUNET_SYSERR == refund_found)
|
||||||
{
|
{
|
||||||
*mhd_ret = reply_refund_conflict (connection,
|
*mhd_ret = TALER_MHD_reply_json_pack (
|
||||||
&refund->coin.coin_pub,
|
connection,
|
||||||
tl);
|
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,
|
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
|
||||||
tl);
|
tl);
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||||
@ -321,7 +281,7 @@ refund_transaction (void *cls,
|
|||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
TALER_EC_REFUND_DB_INCONSISTENT,
|
TALER_EC_REFUND_DB_INCONSISTENT,
|
||||||
"database inconsistent");
|
"database inconsistent (deposit data became inaccessible during transaction)");
|
||||||
return qs;
|
return qs;
|
||||||
}
|
}
|
||||||
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
||||||
@ -352,24 +312,6 @@ refund_transaction (void *cls,
|
|||||||
"refund requested exceeds original value");
|
"refund requested exceeds original value");
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
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,
|
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
|
||||||
tl);
|
tl);
|
||||||
|
|
||||||
@ -405,50 +347,35 @@ static int
|
|||||||
verify_and_execute_refund (struct MHD_Connection *connection,
|
verify_and_execute_refund (struct MHD_Connection *connection,
|
||||||
const struct TALER_EXCHANGEDB_Refund *refund)
|
const struct TALER_EXCHANGEDB_Refund *refund)
|
||||||
{
|
{
|
||||||
struct TALER_EXCHANGEDB_RefundContext rc;
|
|
||||||
struct TALER_RefundRequestPS rr;
|
|
||||||
struct GNUNET_HashCode denom_hash;
|
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);
|
struct TALER_RefundRequestPS rr = {
|
||||||
return TALER_MHD_reply_with_error (connection,
|
.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND),
|
||||||
MHD_HTTP_BAD_REQUEST,
|
.purpose.size = htonl (sizeof (rr)),
|
||||||
TALER_EC_REFUND_FEE_CURRENCY_MISSMATCH,
|
.h_contract_terms = refund->details.h_contract_terms,
|
||||||
"refund_fee");
|
.coin_pub = refund->coin.coin_pub,
|
||||||
}
|
.merchant = refund->details.merchant_pub,
|
||||||
if (-1 == TALER_amount_cmp (&refund->details.refund_amount,
|
.rtransaction_id = GNUNET_htonll (refund->details.rtransaction_id)
|
||||||
&refund->details.refund_fee) )
|
};
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
TALER_amount_hton (&rr.refund_amount,
|
||||||
return TALER_MHD_reply_with_error (connection,
|
&refund->details.refund_amount);
|
||||||
MHD_HTTP_BAD_REQUEST,
|
TALER_amount_hton (&rr.refund_fee,
|
||||||
TALER_EC_REFUND_FEE_ABOVE_AMOUNT,
|
&refund->details.refund_fee);
|
||||||
"refund_amount");
|
if (GNUNET_OK !=
|
||||||
}
|
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
|
||||||
rr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND);
|
&rr.purpose,
|
||||||
rr.purpose.size = htonl (sizeof (struct TALER_RefundRequestPS));
|
&refund->details.merchant_sig.eddsa_sig,
|
||||||
rr.h_contract_terms = refund->details.h_contract_terms;
|
&refund->details.merchant_pub.eddsa_pub))
|
||||||
rr.coin_pub = refund->coin.coin_pub;
|
{
|
||||||
rr.merchant = refund->details.merchant_pub;
|
TALER_LOG_WARNING ("Invalid signature on refund request\n");
|
||||||
rr.rtransaction_id = GNUNET_htonll (refund->details.rtransaction_id);
|
return TALER_MHD_reply_with_error (connection,
|
||||||
TALER_amount_hton (&rr.refund_amount,
|
MHD_HTTP_FORBIDDEN,
|
||||||
&refund->details.refund_amount);
|
TALER_EC_REFUND_MERCHANT_SIGNATURE_INVALID,
|
||||||
TALER_amount_hton (&rr.refund_fee,
|
"merchant_sig");
|
||||||
&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) */
|
/* Fetch the coin's denomination (hash) */
|
||||||
@ -503,23 +430,53 @@ verify_and_execute_refund (struct MHD_Connection *connection,
|
|||||||
ec,
|
ec,
|
||||||
"denomination not found, but coin known");
|
"denomination not found, but coin known");
|
||||||
}
|
}
|
||||||
TALER_amount_ntoh (&rc.expect_fee,
|
TALER_amount_ntoh (&expect_fee,
|
||||||
&dki->issue.properties.fee_refund);
|
&dki->issue.properties.fee_refund);
|
||||||
}
|
}
|
||||||
TEH_KS_release (key_state);
|
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 */
|
/* Finally run the actual transaction logic */
|
||||||
{
|
{
|
||||||
int mhd_ret;
|
int mhd_ret;
|
||||||
|
|
||||||
rc.refund = refund;
|
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
TEH_DB_run_transaction (connection,
|
TEH_DB_run_transaction (connection,
|
||||||
"run refund",
|
"run refund",
|
||||||
&mhd_ret,
|
&mhd_ret,
|
||||||
&refund_transaction,
|
&refund_transaction,
|
||||||
&rc))
|
(void *) refund))
|
||||||
{
|
{
|
||||||
return mhd_ret;
|
return mhd_ret;
|
||||||
}
|
}
|
||||||
@ -546,7 +503,6 @@ TEH_handler_refund (struct MHD_Connection *connection,
|
|||||||
const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
||||||
const json_t *root)
|
const json_t *root)
|
||||||
{
|
{
|
||||||
int res;
|
|
||||||
struct TALER_EXCHANGEDB_Refund refund;
|
struct TALER_EXCHANGEDB_Refund refund;
|
||||||
struct GNUNET_JSON_Specification spec[] = {
|
struct GNUNET_JSON_Specification spec[] = {
|
||||||
TALER_JSON_spec_amount ("refund_amount", &refund.details.refund_amount),
|
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;
|
refund.coin.coin_pub = *coin_pub;
|
||||||
res = TALER_MHD_parse_json_data (connection,
|
{
|
||||||
root,
|
int res;
|
||||||
spec);
|
|
||||||
if (GNUNET_SYSERR == res)
|
res = TALER_MHD_parse_json_data (connection,
|
||||||
return MHD_NO; /* hard failure */
|
root,
|
||||||
if (GNUNET_NO == res)
|
spec);
|
||||||
return MHD_YES; /* failure */
|
if (GNUNET_SYSERR == res)
|
||||||
res = verify_and_execute_refund (connection,
|
return MHD_NO; /* hard failure */
|
||||||
&refund);
|
if (GNUNET_NO == res)
|
||||||
GNUNET_JSON_parse_free (spec);
|
return MHD_YES; /* failure */
|
||||||
return res;
|
}
|
||||||
|
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
|
* @file taler-exchange-httpd_withdraw.c
|
||||||
* @brief Handle /reserve/withdraw requests
|
* @brief Handle /reserves/$RESERVE_PUB/withdraw requests
|
||||||
* @author Florian Dold
|
* @author Florian Dold
|
||||||
* @author Benedikt Mueller
|
* @author Benedikt Mueller
|
||||||
* @author Christian Grothoff
|
* @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
|
* message that we have insufficient funds for the
|
||||||
* requested withdraw operation.
|
* requested withdraw operation.
|
||||||
*
|
*
|
||||||
@ -52,10 +52,10 @@
|
|||||||
* @return MHD result code
|
* @return MHD result code
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
reply_reserve_withdraw_insufficient_funds (struct MHD_Connection *connection,
|
reply_withdraw_insufficient_funds (
|
||||||
const struct TALER_Amount *ebalance,
|
struct MHD_Connection *connection,
|
||||||
const struct
|
const struct TALER_Amount *ebalance,
|
||||||
TALER_EXCHANGEDB_ReserveHistory *rh)
|
const struct TALER_EXCHANGEDB_ReserveHistory *rh)
|
||||||
{
|
{
|
||||||
json_t *json_history;
|
json_t *json_history;
|
||||||
struct TALER_Amount balance;
|
struct TALER_Amount balance;
|
||||||
@ -66,7 +66,7 @@ reply_reserve_withdraw_insufficient_funds (struct MHD_Connection *connection,
|
|||||||
return TALER_MHD_reply_with_error (connection,
|
return TALER_MHD_reply_with_error (connection,
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
TALER_EC_WITHDRAW_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS,
|
TALER_EC_WITHDRAW_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS,
|
||||||
"balance calculation failure");
|
"reserve balance calculation failure");
|
||||||
if (0 !=
|
if (0 !=
|
||||||
TALER_amount_cmp (&balance,
|
TALER_amount_cmp (&balance,
|
||||||
ebalance))
|
ebalance))
|
||||||
@ -75,7 +75,7 @@ reply_reserve_withdraw_insufficient_funds (struct MHD_Connection *connection,
|
|||||||
json_decref (json_history);
|
json_decref (json_history);
|
||||||
return TALER_MHD_reply_with_error (connection,
|
return TALER_MHD_reply_with_error (connection,
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
TALER_EC_WITHDRAW_HISTORY_RESERVE_BALANCE_CORRUPT,
|
TALER_EC_WITHDRAW_RESERVE_BALANCE_CORRUPT,
|
||||||
"internal balance inconsistency error");
|
"internal balance inconsistency error");
|
||||||
}
|
}
|
||||||
return TALER_MHD_reply_json_pack (connection,
|
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 connection connection to the client
|
||||||
* @param collectable blinded coin to return
|
* @param collectable blinded coin to return
|
||||||
* @return MHD result code
|
* @return MHD result code
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
reply_reserve_withdraw_success (struct MHD_Connection *connection,
|
reply_withdraw_success (
|
||||||
const struct
|
struct MHD_Connection *connection,
|
||||||
TALER_EXCHANGEDB_CollectableBlindcoin *
|
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable)
|
||||||
collectable)
|
|
||||||
{
|
{
|
||||||
json_t *sig_json;
|
json_t *sig_json;
|
||||||
|
|
||||||
sig_json = GNUNET_JSON_from_rsa_signature (collectable->sig.rsa_signature);
|
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,
|
return TALER_MHD_reply_json_pack (connection,
|
||||||
MHD_HTTP_OK,
|
MHD_HTTP_OK,
|
||||||
"{s:o}",
|
"{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
|
* transaction logic; IF it returns a non-error code, the transaction
|
||||||
* logic MUST NOT queue a MHD response. IF it returns an hard error,
|
* 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.
|
* 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
|
* before entering the transaction, or because this function is run
|
||||||
* twice (!) by #TEH_DB_run_transaction() and the first time created
|
* twice (!) by #TEH_DB_run_transaction() and the first time created
|
||||||
* the signature and then failed to commit. Furthermore, we may get
|
* 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
|
* 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.
|
* The caller must thus free the signature in either case.
|
||||||
*
|
*
|
||||||
* @param cls a `struct WithdrawContext *`
|
* @param cls a `struct WithdrawContext *`
|
||||||
@ -201,7 +205,6 @@ withdraw_transaction (void *cls,
|
|||||||
struct WithdrawContext *wc = cls;
|
struct WithdrawContext *wc = cls;
|
||||||
struct TALER_EXCHANGEDB_Reserve r;
|
struct TALER_EXCHANGEDB_Reserve r;
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
struct TALER_Amount fee_withdraw;
|
|
||||||
struct TALER_DenominationSignature denom_sig;
|
struct TALER_DenominationSignature denom_sig;
|
||||||
|
|
||||||
#if OPTIMISTIC_SIGN
|
#if OPTIMISTIC_SIGN
|
||||||
@ -227,18 +230,23 @@ withdraw_transaction (void *cls,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Don't sign again if we have already signed the coin */
|
/* 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
|
#if OPTIMISTIC_SIGN
|
||||||
GNUNET_CRYPTO_rsa_signature_free (denom_sig.rsa_signature);
|
GNUNET_CRYPTO_rsa_signature_free (denom_sig.rsa_signature);
|
||||||
#endif
|
#endif
|
||||||
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
|
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
|
||||||
}
|
}
|
||||||
GNUNET_assert (0 == qs);
|
/* We should never get more than one result, and we handled
|
||||||
wc->collectable.sig = denom_sig;
|
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 */
|
/* 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,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
"Trying to withdraw from reserve: %s\n",
|
"Trying to withdraw from reserve: %s\n",
|
||||||
TALER_B2S (&r.pub));
|
TALER_B2S (&r.pub));
|
||||||
@ -265,20 +273,25 @@ withdraw_transaction (void *cls,
|
|||||||
if (0 < TALER_amount_cmp (&wc->amount_required,
|
if (0 < TALER_amount_cmp (&wc->amount_required,
|
||||||
&r.balance))
|
&r.balance))
|
||||||
{
|
{
|
||||||
char *amount_required;
|
|
||||||
char *r_balance;
|
|
||||||
struct TALER_EXCHANGEDB_ReserveHistory *rh;
|
struct TALER_EXCHANGEDB_ReserveHistory *rh;
|
||||||
|
|
||||||
/* The reserve does not have the required amount (actual
|
/* The reserve does not have the required amount (actual
|
||||||
* amount + withdraw fee) */
|
* amount + withdraw fee) */
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
amount_required = TALER_amount_to_string (&wc->amount_required);
|
#if GNUNET_EXTRA_LOGGING
|
||||||
r_balance = TALER_amount_to_string (&r.balance);
|
{
|
||||||
TALER_LOG_WARNING ("Asked %s over a reserve worth %s\n",
|
char *amount_required;
|
||||||
amount_required,
|
char *r_balance;
|
||||||
r_balance);
|
|
||||||
GNUNET_free (amount_required);
|
amount_required = TALER_amount_to_string (&wc->amount_required);
|
||||||
GNUNET_free (r_balance);
|
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,
|
qs = TEH_plugin->get_reserve_history (TEH_plugin->cls,
|
||||||
session,
|
session,
|
||||||
&wc->wsrd.reserve_pub,
|
&wc->wsrd.reserve_pub,
|
||||||
@ -292,9 +305,9 @@ withdraw_transaction (void *cls,
|
|||||||
"failed to fetch reserve history");
|
"failed to fetch reserve history");
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||||
}
|
}
|
||||||
*mhd_ret = reply_reserve_withdraw_insufficient_funds (connection,
|
*mhd_ret = reply_withdraw_insufficient_funds (connection,
|
||||||
&r.balance,
|
&r.balance,
|
||||||
rh);
|
rh);
|
||||||
TEH_plugin->free_reserve_history (TEH_plugin->cls,
|
TEH_plugin->free_reserve_history (TEH_plugin->cls,
|
||||||
rh);
|
rh);
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||||
@ -314,16 +327,15 @@ withdraw_transaction (void *cls,
|
|||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
TALER_EC_WITHDRAW_SIGNATURE_FAILED,
|
TALER_EC_WITHDRAW_SIGNATURE_FAILED,
|
||||||
"Failed to create signature");
|
"Failed to create blind signature");
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
TALER_amount_ntoh (&fee_withdraw,
|
|
||||||
&wc->dki->issue.properties.fee_withdraw);
|
|
||||||
wc->collectable.denom_pub_hash = wc->denom_pub_hash;
|
wc->collectable.denom_pub_hash = wc->denom_pub_hash;
|
||||||
wc->collectable.amount_with_fee = wc->amount_required;
|
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.reserve_pub = wc->wsrd.reserve_pub;
|
||||||
wc->collectable.h_coin_envelope = wc->wsrd.h_coin_envelope;
|
wc->collectable.h_coin_envelope = wc->wsrd.h_coin_envelope;
|
||||||
wc->collectable.reserve_sig = wc->signature;
|
wc->collectable.reserve_sig = wc->signature;
|
||||||
@ -366,12 +378,6 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh,
|
|||||||
const char *const args[2])
|
const char *const args[2])
|
||||||
{
|
{
|
||||||
struct WithdrawContext wc;
|
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[] = {
|
struct GNUNET_JSON_Specification spec[] = {
|
||||||
GNUNET_JSON_spec_varsize ("coin_ev",
|
GNUNET_JSON_spec_varsize ("coin_ev",
|
||||||
(void **) &wc.blinded_msg,
|
(void **) &wc.blinded_msg,
|
||||||
@ -397,11 +403,15 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh,
|
|||||||
"reserve public key malformed");
|
"reserve public key malformed");
|
||||||
}
|
}
|
||||||
|
|
||||||
res = TALER_MHD_parse_json_data (connection,
|
{
|
||||||
root,
|
int res;
|
||||||
spec);
|
|
||||||
if (GNUNET_OK != res)
|
res = TALER_MHD_parse_json_data (connection,
|
||||||
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
|
root,
|
||||||
|
spec);
|
||||||
|
if (GNUNET_OK != res)
|
||||||
|
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
|
||||||
|
}
|
||||||
wc.key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ());
|
wc.key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ());
|
||||||
if (NULL == wc.key_state)
|
if (NULL == wc.key_state)
|
||||||
{
|
{
|
||||||
@ -412,41 +422,52 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh,
|
|||||||
TALER_EC_EXCHANGE_BAD_CONFIGURATION,
|
TALER_EC_EXCHANGE_BAD_CONFIGURATION,
|
||||||
"no keys");
|
"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);
|
unsigned int hc;
|
||||||
TEH_KS_release (wc.key_state);
|
enum TALER_ErrorCode ec;
|
||||||
return TALER_MHD_reply_with_error (connection,
|
|
||||||
hc,
|
wc.dki = TEH_KS_denomination_key_lookup_by_hash (wc.key_state,
|
||||||
ec,
|
&wc.denom_pub_hash,
|
||||||
"could not find denomination key");
|
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);
|
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);
|
struct TALER_Amount amount;
|
||||||
TEH_KS_release (wc.key_state);
|
struct TALER_Amount fee_withdraw;
|
||||||
return TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
TALER_amount_ntoh (&amount,
|
||||||
TALER_EC_WITHDRAW_AMOUNT_FEE_OVERFLOW,
|
&wc.dki->issue.properties.value);
|
||||||
"amount overflow for value plus withdraw fee");
|
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! */
|
/* verify signature! */
|
||||||
wc.wsrd.purpose.size
|
wc.wsrd.purpose.size
|
||||||
= htonl (sizeof (struct TALER_WithdrawRequestPS));
|
= htonl (sizeof (struct TALER_WithdrawRequestPS));
|
||||||
@ -491,28 +512,39 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (GNUNET_OK !=
|
/* run transaction and sign (if not optimistically signed before) */
|
||||||
TEH_DB_run_transaction (connection,
|
|
||||||
"run withdraw",
|
|
||||||
&mhd_ret,
|
|
||||||
&withdraw_transaction,
|
|
||||||
&wc))
|
|
||||||
{
|
{
|
||||||
TEH_KS_release (wc.key_state);
|
int mhd_ret;
|
||||||
/* Even if #withdraw_transaction() failed, it may have created a signature
|
|
||||||
(or we might have done it optimistically above). */
|
if (GNUNET_OK !=
|
||||||
if (NULL != wc.collectable.sig.rsa_signature)
|
TEH_DB_run_transaction (connection,
|
||||||
GNUNET_CRYPTO_rsa_signature_free (wc.collectable.sig.rsa_signature);
|
"run withdraw",
|
||||||
GNUNET_JSON_parse_free (spec);
|
&mhd_ret,
|
||||||
return 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);
|
TEH_KS_release (wc.key_state);
|
||||||
GNUNET_JSON_parse_free (spec);
|
GNUNET_JSON_parse_free (spec);
|
||||||
|
|
||||||
mhd_ret = reply_reserve_withdraw_success (connection,
|
{
|
||||||
&wc.collectable);
|
int ret;
|
||||||
GNUNET_CRYPTO_rsa_signature_free (wc.collectable.sig.rsa_signature);
|
|
||||||
return mhd_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,
|
TALER_EC_DENOMINATION_KEY_LOST = 1116,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The exchange's database entry with the reserve balance summary
|
* The exchange's database entry with the reserve balance summary is
|
||||||
* is inconsistent with its own history of the reserve.
|
* inconsistent with its own history of the reserve. Returned with an
|
||||||
* Returned with an HTTP status of #MHD_HTTP_INTERNAL_SERVER_ERROR.
|
* 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
|
* 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,
|
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
|
* The respective coin did not have sufficient residual value for the
|
||||||
* /refresh/melt operation. The "history" in this response provdes
|
* /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
|
* "original_value". This response is provided with HTTP status code
|
||||||
* MHD_HTTP_CONFLICT.
|
* 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
|
* The exchange had an internal error reconstructing the transaction
|
||||||
@ -607,6 +614,13 @@ enum TALER_ErrorCode
|
|||||||
*/
|
*/
|
||||||
TALER_EC_MELT_INVALID_SIGNATURE_BY_EXCHANGE = 1310,
|
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
|
* The exchange is unaware of the denomination key that was used to
|
||||||
* sign the melted zombie coin. This response is provided with HTTP
|
* sign the melted zombie coin. This response is provided with HTTP
|
||||||
|
Loading…
Reference in New Issue
Block a user