fixing #5010 for /reserve/withdraw

This commit is contained in:
Christian Grothoff 2017-06-19 00:00:21 +02:00
parent 4cb035cd29
commit dea0f7c411
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
11 changed files with 510 additions and 642 deletions

View File

@ -3576,7 +3576,7 @@ run (void *cls)
GNUNET_assert (NULL != exchange); GNUNET_assert (NULL != exchange);
timeout_task timeout_task
= GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
(GNUNET_TIME_UNIT_SECONDS, 150), (GNUNET_TIME_UNIT_SECONDS, 300),
&do_timeout, NULL); &do_timeout, NULL);
GNUNET_SCHEDULER_add_shutdown (&do_shutdown, is); GNUNET_SCHEDULER_add_shutdown (&do_shutdown, is);
} }

View File

@ -552,350 +552,6 @@ TEH_DB_execute_refund (struct MHD_Connection *connection,
} }
/**
* Try to execute /reserve/withdraw transaction.
*
* @param connection request we are handling
* @param session database session we are using
* @param key_state key state to lookup denomination pubs
* @param reserve reserve to withdraw from
* @param denomination_pub public key of the denomination requested
* @param dki denomination to withdraw
* @param blinded_msg blinded message to be signed
* @param blinded_msg_len number of bytes in @a blinded_msg
* @param h_blind hash of @a blinded_msg
* @param signature signature over the withdraw request, to be stored in DB
* @param[out] denom_sig where to write the resulting signature
* (used to release memory in case of transaction failure
* @return MHD result code
*/
static int
execute_reserve_withdraw_transaction (struct MHD_Connection *connection,
struct TALER_EXCHANGEDB_Session *session,
struct TEH_KS_StateHandle *key_state,
const struct TALER_ReservePublicKeyP *reserve,
const struct TALER_DenominationPublicKey *denomination_pub,
const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki,
const char *blinded_msg,
size_t blinded_msg_len,
const struct GNUNET_HashCode *h_blind,
const struct TALER_ReserveSignatureP *signature,
struct TALER_DenominationSignature *denom_sig)
{
struct TALER_EXCHANGEDB_ReserveHistory *rh;
const struct TALER_EXCHANGEDB_ReserveHistory *pos;
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *tdki;
struct TALER_EXCHANGEDB_CollectableBlindcoin collectable;
struct TALER_Amount amount_required;
struct TALER_Amount deposit_total;
struct TALER_Amount withdraw_total;
struct TALER_Amount balance;
struct TALER_Amount value;
struct TALER_Amount fee_withdraw;
int res;
int ret;
enum GNUNET_DB_QueryStatus qs;
/* Check if balance is sufficient */
START_TRANSACTION (session, connection);
qs = TEH_plugin->get_reserve_history (TEH_plugin->cls,
session,
reserve,
&rh);
(void) qs;
/* qs: #5010! */
if (NULL == rh)
{
TEH_plugin->rollback (TEH_plugin->cls,
session);
return TEH_RESPONSE_reply_arg_unknown (connection,
TALER_EC_WITHDRAW_RESERVE_UNKNOWN,
"reserve_pub");
}
/* calculate amount required including fees */
TALER_amount_ntoh (&value,
&dki->issue.properties.value);
TALER_amount_ntoh (&fee_withdraw,
&dki->issue.properties.fee_withdraw);
if (GNUNET_OK !=
TALER_amount_add (&amount_required,
&value,
&fee_withdraw))
{
TEH_plugin->rollback (TEH_plugin->cls,
session);
return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_WITHDRAW_AMOUNT_FEE_OVERFLOW);
}
/* calculate balance of the reserve */
res = 0;
for (pos = rh; NULL != pos; pos = pos->next)
{
switch (pos->type)
{
case TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE:
if (0 == (res & 1))
deposit_total = pos->details.bank->amount;
else
if (GNUNET_OK !=
TALER_amount_add (&deposit_total,
&deposit_total,
&pos->details.bank->amount))
{
TEH_plugin->rollback (TEH_plugin->cls,
session);
return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_WITHDRAW_AMOUNT_DEPOSITS_OVERFLOW);
}
res |= 1;
break;
case TALER_EXCHANGEDB_RO_WITHDRAW_COIN:
tdki = TEH_KS_denomination_key_lookup (key_state,
&pos->details.withdraw->denom_pub,
TEH_KS_DKU_WITHDRAW);
if (NULL == tdki)
{
GNUNET_break (0);
TEH_plugin->rollback (TEH_plugin->cls,
session);
return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_WITHDRAW_HISTORIC_DENOMINATION_KEY_NOT_FOUND);
}
TALER_amount_ntoh (&value,
&tdki->issue.properties.value);
if (0 == (res & 2))
withdraw_total = value;
else
if (GNUNET_OK !=
TALER_amount_add (&withdraw_total,
&withdraw_total,
&value))
{
TEH_plugin->rollback (TEH_plugin->cls,
session);
return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_WITHDRAW_AMOUNT_WITHDRAWALS_OVERFLOW);
}
res |= 2;
break;
case TALER_EXCHANGEDB_RO_PAYBACK_COIN:
if (0 == (res & 1))
deposit_total = pos->details.payback->value;
else
if (GNUNET_OK !=
TALER_amount_add (&deposit_total,
&deposit_total,
&pos->details.payback->value))
{
TEH_plugin->rollback (TEH_plugin->cls,
session);
return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_WITHDRAW_AMOUNT_DEPOSITS_OVERFLOW);
}
res |= 1;
break;
case TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK:
if (0 == (res & 2))
withdraw_total = pos->details.bank->amount;
else
if (GNUNET_OK !=
TALER_amount_add (&withdraw_total,
&withdraw_total,
&pos->details.bank->amount))
{
TEH_plugin->rollback (TEH_plugin->cls,
session);
return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_WITHDRAW_AMOUNT_WITHDRAWALS_OVERFLOW);
}
res |= 2;
break;
}
}
if (0 == (res & 1))
{
/* did not encounter any wire transfer operations, how can we have a reserve? */
GNUNET_break (0);
TEH_plugin->rollback (TEH_plugin->cls,
session);
return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_WITHDRAW_RESERVE_WITHOUT_WIRE_TRANSFER);
}
if (0 == (res & 2))
{
/* did not encounter any withdraw operations, set to zero */
TALER_amount_get_zero (deposit_total.currency,
&withdraw_total);
}
/* All reserve balances should be non-negative */
if (GNUNET_SYSERR ==
TALER_amount_subtract (&balance,
&deposit_total,
&withdraw_total))
{
GNUNET_break (0); /* database inconsistent */
TEH_plugin->rollback (TEH_plugin->cls,
session);
return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_WITHDRAW_RESERVE_HISTORY_IMPOSSIBLE);
}
if (0 < TALER_amount_cmp (&amount_required,
&balance))
{
TEH_plugin->rollback (TEH_plugin->cls,
session);
res = TEH_RESPONSE_reply_reserve_withdraw_insufficient_funds (connection,
rh);
TEH_plugin->free_reserve_history (TEH_plugin->cls,
rh);
return res;
}
TEH_plugin->free_reserve_history (TEH_plugin->cls,
rh);
/* Balance is good, sign the coin! */
denom_sig->rsa_signature
= GNUNET_CRYPTO_rsa_sign_blinded (dki->denom_priv.rsa_private_key,
blinded_msg,
blinded_msg_len);
if (NULL == denom_sig->rsa_signature)
{
GNUNET_break (0);
TEH_plugin->rollback (TEH_plugin->cls,
session);
return TEH_RESPONSE_reply_internal_error (connection,
TALER_EC_WITHDRAW_SIGNATURE_FAILED,
"Internal error");
}
collectable.sig = *denom_sig;
collectable.denom_pub = *denomination_pub;
collectable.amount_with_fee = amount_required;
collectable.withdraw_fee = fee_withdraw;
collectable.reserve_pub = *reserve;
collectable.h_coin_envelope = *h_blind;
collectable.reserve_sig = *signature;
ret = TEH_plugin->insert_withdraw_info (TEH_plugin->cls,
session,
&collectable);
if (GNUNET_SYSERR == ret)
{
GNUNET_break (0);
TEH_plugin->rollback (TEH_plugin->cls,
session);
return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_WITHDRAW_DB_STORE_ERROR);
}
if (GNUNET_NO == ret)
RETRY_TRANSACTION(session, connection);
COMMIT_TRANSACTION (session, connection);
return TEH_RESPONSE_reply_reserve_withdraw_success (connection,
&collectable);
}
/**
* Execute a "/reserve/withdraw". Given a reserve and a properly signed
* request to withdraw a coin, check the balance of the reserve and
* if it is sufficient, store the request and return the signed
* blinded envelope.
*
* @param connection the MHD connection to handle
* @param reserve public key of the reserve
* @param denomination_pub public key of the denomination requested
* @param blinded_msg blinded message to be signed
* @param blinded_msg_len number of bytes in @a blinded_msg
* @param signature signature over the withdraw request, to be stored in DB
* @return MHD result code
*/
int
TEH_DB_execute_reserve_withdraw (struct MHD_Connection *connection,
const struct TALER_ReservePublicKeyP *reserve,
const struct TALER_DenominationPublicKey *denomination_pub,
const char *blinded_msg,
size_t blinded_msg_len,
const struct TALER_ReserveSignatureP *signature)
{
struct TALER_EXCHANGEDB_Session *session;
struct TEH_KS_StateHandle *key_state;
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
struct TALER_EXCHANGEDB_CollectableBlindcoin collectable;
struct TALER_DenominationSignature denom_sig;
struct GNUNET_HashCode h_blind;
int res;
GNUNET_CRYPTO_hash (blinded_msg,
blinded_msg_len,
&h_blind);
if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
{
GNUNET_break (0);
return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_DB_SETUP_FAILED);
}
res = TEH_plugin->get_withdraw_info (TEH_plugin->cls,
session,
&h_blind,
&collectable);
if (GNUNET_SYSERR == res)
{
GNUNET_break (0);
return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_WITHDRAW_DB_FETCH_ERROR);
}
/* Don't sign again if we have already signed the coin */
if (GNUNET_YES == res)
{
res = TEH_RESPONSE_reply_reserve_withdraw_success (connection,
&collectable);
GNUNET_CRYPTO_rsa_signature_free (collectable.sig.rsa_signature);
GNUNET_CRYPTO_rsa_public_key_free (collectable.denom_pub.rsa_public_key);
return res;
}
GNUNET_assert (GNUNET_NO == res);
/* FIXME: do we have to do this a second time here? */
key_state = TEH_KS_acquire ();
dki = TEH_KS_denomination_key_lookup (key_state,
denomination_pub,
TEH_KS_DKU_WITHDRAW);
if (NULL == dki)
{
TEH_KS_release (key_state);
return TEH_RESPONSE_reply_json_pack (connection,
MHD_HTTP_NOT_FOUND,
"{s:s, s:I}",
"error",
"Denomination not found",
"code",
(json_int_t) TALER_EC_WITHDRAW_DENOMINATION_KEY_NOT_FOUND);
}
denom_sig.rsa_signature = NULL;
res = execute_reserve_withdraw_transaction (connection,
session,
key_state,
reserve,
denomination_pub,
dki,
blinded_msg,
blinded_msg_len,
&h_blind,
signature,
&denom_sig);
if (NULL != denom_sig.rsa_signature)
GNUNET_CRYPTO_rsa_signature_free (denom_sig.rsa_signature);
TEH_KS_release (key_state);
return res;
}
/** /**
* Parse coin melt requests from a JSON object and write them to * Parse coin melt requests from a JSON object and write them to
* the database. * the database.

View File

@ -97,29 +97,6 @@ TEH_DB_execute_refund (struct MHD_Connection *connection,
const struct TALER_EXCHANGEDB_Refund *refund); const struct TALER_EXCHANGEDB_Refund *refund);
/**
* Execute a "/reserve/withdraw". Given a reserve and a properly signed
* request to withdraw a coin, check the balance of the reserve and
* if it is sufficient, store the request and return the signed
* blinded envelope.
*
* @param connection the MHD connection to handle
* @param reserve public key of the reserve
* @param denomination_pub public key of the denomination requested
* @param blinded_msg blinded message to be signed
* @param blinded_msg_len number of bytes in @a blinded_msg
* @param signature signature over the withdraw request, to be stored in DB
* @return MHD result code
*/
int
TEH_DB_execute_reserve_withdraw (struct MHD_Connection *connection,
const struct TALER_ReservePublicKeyP *reserve,
const struct TALER_DenominationPublicKey *denomination_pub,
const char *blinded_msg,
size_t blinded_msg_len,
const struct TALER_ReserveSignatureP *signature);
/** /**
* @brief Details about a melt operation of an individual coin. * @brief Details about a melt operation of an individual coin.
*/ */

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014, 2015, 2016 GNUnet e.V. Copyright (C) 2014-2017 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software terms of the GNU Affero General Public License as published by the Free Software
@ -29,6 +29,36 @@
#include "taler-exchange-httpd_keystate.h" #include "taler-exchange-httpd_keystate.h"
/**
* Send reserve status information to client.
*
* @param connection connection to the client
* @param rh reserve history to return
* @return MHD result code
*/
static int
reply_reserve_status_success (struct MHD_Connection *connection,
const struct TALER_EXCHANGEDB_ReserveHistory *rh)
{
json_t *json_balance;
json_t *json_history;
struct TALER_Amount balance;
json_history = TEH_RESPONSE_compile_reserve_history (rh,
&balance);
if (NULL == json_history)
return TEH_RESPONSE_reply_internal_error (connection,
TALER_EC_RESERVE_STATUS_DB_ERROR,
"balance calculation failure");
json_balance = TALER_JSON_from_amount (&balance);
return TEH_RESPONSE_reply_json_pack (connection,
MHD_HTTP_OK,
"{s:o, s:o}",
"balance", json_balance,
"history", json_history);
}
/** /**
* Closure for #reserve_status_transaction. * Closure for #reserve_status_transaction.
*/ */
@ -126,7 +156,7 @@ TEH_RESERVE_handler_reserve_status (struct TEH_RequestHandler *rh,
"{s:s, s:s}", "{s:s, s:s}",
"error", "Reserve not found", "error", "Reserve not found",
"parameter", "withdraw_pub"); "parameter", "withdraw_pub");
mhd_ret = TEH_RESPONSE_reply_reserve_status_success (connection, mhd_ret = reply_reserve_status_success (connection,
rsc.rh); rsc.rh);
TEH_plugin->free_reserve_history (TEH_plugin->cls, TEH_plugin->free_reserve_history (TEH_plugin->cls,
rsc.rh); rsc.rh);

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014, 2015, 2016 GNUnet e.V. Copyright (C) 2014-2017 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software terms of the GNU Affero General Public License as published by the Free Software
@ -29,6 +29,348 @@
#include "taler-exchange-httpd_keystate.h" #include "taler-exchange-httpd_keystate.h"
/**
* Send reserve status information to client with the
* message that we have insufficient funds for the
* requested /reserve/withdraw operation.
*
* @param connection connection to the client
* @param rh reserve history to return
* @return MHD result code
*/
static int
reply_reserve_withdraw_insufficient_funds (struct MHD_Connection *connection,
const struct TALER_EXCHANGEDB_ReserveHistory *rh)
{
json_t *json_balance;
json_t *json_history;
struct TALER_Amount balance;
json_history = TEH_RESPONSE_compile_reserve_history (rh,
&balance);
if (NULL == json_history)
return TEH_RESPONSE_reply_internal_error (connection,
TALER_EC_WITHDRAW_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS,
"balance calculation failure");
json_balance = TALER_JSON_from_amount (&balance);
return TEH_RESPONSE_reply_json_pack (connection,
MHD_HTTP_FORBIDDEN,
"{s:s, s:I, s:o, s:o}",
"error", "Insufficient funds",
"code", (json_int_t) TALER_EC_WITHDRAW_INSUFFICIENT_FUNDS,
"balance", json_balance,
"history", json_history);
}
/**
* Send blinded coin information 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)
{
json_t *sig_json;
sig_json = GNUNET_JSON_from_rsa_signature (collectable->sig.rsa_signature);
return TEH_RESPONSE_reply_json_pack (connection,
MHD_HTTP_OK,
"{s:o}",
"ev_sig", sig_json);
}
/**
* Context for #withdraw_transaction.
*/
struct WithdrawContext
{
/**
* Details about the withdrawal request.
*/
struct TALER_WithdrawRequestPS wsrd;
/**
* Value of the coin plus withdraw fee.
*/
struct TALER_Amount amount_required;
/**
* Denomination public key.
*/
struct TALER_DenominationPublicKey denomination_pub;
/**
* Signature over the request.
*/
struct TALER_ReserveSignatureP signature;
/**
* Blinded planchet.
*/
char *blinded_msg;
/**
* Key state to use to inspect previous withdrawal values.
*/
struct TEH_KS_StateHandle *key_state;
/**
* Number of bytes in @e blinded_msg.
*/
size_t blinded_msg_len;
/**
* Details about denomination we are about to withdraw.
*/
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
/**
* Set to the resulting signed coin data to be returned to the client.
*/
struct TALER_EXCHANGEDB_CollectableBlindcoin collectable;
};
/**
* Function implementing /reserve/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.
* IF it returns the soft error code, the function MAY be called again
* to retry and MUST not queue a MHD response.
*
* @param cls a `struct WithdrawContext *`
* @param connection MHD request which triggered the transaction
* @param session database session to use
* @param[out] mhd_ret set to MHD response status for @a connection,
* if transaction failed (!)
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
withdraw_transaction (void *cls,
struct MHD_Connection *connection,
struct TALER_EXCHANGEDB_Session *session,
int *mhd_ret)
{
struct WithdrawContext *wc = cls;
struct TALER_EXCHANGEDB_ReserveHistory *rh;
struct TALER_Amount deposit_total;
struct TALER_Amount withdraw_total;
struct TALER_Amount balance;
struct TALER_Amount value;
struct TALER_Amount fee_withdraw;
int res;
enum GNUNET_DB_QueryStatus qs;
struct TALER_DenominationSignature denom_sig;
struct GNUNET_HashCode h_blind;
GNUNET_CRYPTO_hash (wc->blinded_msg,
wc->blinded_msg_len,
&h_blind);
qs = TEH_plugin->get_withdraw_info (TEH_plugin->cls,
session,
&h_blind,
&wc->collectable);
if (0 > qs)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
*mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_WITHDRAW_DB_FETCH_ERROR);
return qs;
}
/* Don't sign again if we have already signed the coin */
if (1 == qs)
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
GNUNET_assert (0 == qs);
/* Check if balance is sufficient */
qs = TEH_plugin->get_reserve_history (TEH_plugin->cls,
session,
&wc->wsrd.reserve_pub,
&rh);
if (0 > qs)
{
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
*mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_WITHDRAW_DB_FETCH_ERROR);
return qs;
}
if (NULL == rh)
{
*mhd_ret = TEH_RESPONSE_reply_arg_unknown (connection,
TALER_EC_WITHDRAW_RESERVE_UNKNOWN,
"reserve_pub");
return GNUNET_DB_STATUS_HARD_ERROR;
}
/* calculate balance of the reserve */
res = 0;
for (const struct TALER_EXCHANGEDB_ReserveHistory *pos = rh;
NULL != pos;
pos = pos->next)
{
switch (pos->type)
{
case TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE:
if (0 == (res & 1))
deposit_total = pos->details.bank->amount;
else
if (GNUNET_OK !=
TALER_amount_add (&deposit_total,
&deposit_total,
&pos->details.bank->amount))
{
*mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_WITHDRAW_AMOUNT_DEPOSITS_OVERFLOW);
return GNUNET_DB_STATUS_HARD_ERROR;
}
res |= 1;
break;
case TALER_EXCHANGEDB_RO_WITHDRAW_COIN:
{
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *tdki;
tdki = TEH_KS_denomination_key_lookup (wc->key_state,
&pos->details.withdraw->denom_pub,
TEH_KS_DKU_WITHDRAW);
if (NULL == tdki)
{
GNUNET_break (0);
*mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_WITHDRAW_HISTORIC_DENOMINATION_KEY_NOT_FOUND);
return GNUNET_DB_STATUS_HARD_ERROR;
}
TALER_amount_ntoh (&value,
&tdki->issue.properties.value);
if (0 == (res & 2))
withdraw_total = value;
else
if (GNUNET_OK !=
TALER_amount_add (&withdraw_total,
&withdraw_total,
&value))
{
*mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_WITHDRAW_AMOUNT_WITHDRAWALS_OVERFLOW);
return GNUNET_DB_STATUS_HARD_ERROR;
}
res |= 2;
break;
}
case TALER_EXCHANGEDB_RO_PAYBACK_COIN:
if (0 == (res & 1))
deposit_total = pos->details.payback->value;
else
if (GNUNET_OK !=
TALER_amount_add (&deposit_total,
&deposit_total,
&pos->details.payback->value))
{
*mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_WITHDRAW_AMOUNT_DEPOSITS_OVERFLOW);
return GNUNET_DB_STATUS_HARD_ERROR;
}
res |= 1;
break;
case TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK:
if (0 == (res & 2))
withdraw_total = pos->details.bank->amount;
else
if (GNUNET_OK !=
TALER_amount_add (&withdraw_total,
&withdraw_total,
&pos->details.bank->amount))
{
*mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_WITHDRAW_AMOUNT_WITHDRAWALS_OVERFLOW);
return GNUNET_DB_STATUS_HARD_ERROR;
}
res |= 2;
break;
}
}
if (0 == (res & 1))
{
/* did not encounter any wire transfer operations, how can we have a reserve? */
GNUNET_break (0);
*mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_WITHDRAW_RESERVE_WITHOUT_WIRE_TRANSFER);
return GNUNET_DB_STATUS_HARD_ERROR;
}
if (0 == (res & 2))
{
/* did not encounter any withdraw operations, set to zero */
TALER_amount_get_zero (deposit_total.currency,
&withdraw_total);
}
/* All reserve balances should be non-negative */
if (GNUNET_SYSERR ==
TALER_amount_subtract (&balance,
&deposit_total,
&withdraw_total))
{
GNUNET_break (0); /* database inconsistent */
*mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_WITHDRAW_RESERVE_HISTORY_IMPOSSIBLE);
return GNUNET_DB_STATUS_HARD_ERROR;
}
if (0 < TALER_amount_cmp (&wc->amount_required,
&balance))
{
*mhd_ret = reply_reserve_withdraw_insufficient_funds (connection,
rh);
TEH_plugin->free_reserve_history (TEH_plugin->cls,
rh);
return GNUNET_DB_STATUS_HARD_ERROR;
}
TEH_plugin->free_reserve_history (TEH_plugin->cls,
rh);
/* Balance is good, sign the coin! */
denom_sig.rsa_signature
= GNUNET_CRYPTO_rsa_sign_blinded (wc->dki->denom_priv.rsa_private_key,
wc->blinded_msg,
wc->blinded_msg_len);
if (NULL == denom_sig.rsa_signature)
{
GNUNET_break (0);
*mhd_ret = TEH_RESPONSE_reply_internal_error (connection,
TALER_EC_WITHDRAW_SIGNATURE_FAILED,
"Internal error");
return GNUNET_DB_STATUS_HARD_ERROR;
}
wc->collectable.sig = denom_sig;
wc->collectable.denom_pub = wc->denomination_pub;
wc->collectable.amount_with_fee = wc->amount_required;
wc->collectable.withdraw_fee = fee_withdraw;
wc->collectable.reserve_pub = wc->wsrd.reserve_pub;
wc->collectable.h_coin_envelope = h_blind;
wc->collectable.reserve_sig = wc->signature;
qs = TEH_plugin->insert_withdraw_info (TEH_plugin->cls,
session,
&wc->collectable);
if (0 > qs)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
GNUNET_CRYPTO_rsa_signature_free (denom_sig.rsa_signature);
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
*mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_WITHDRAW_DB_STORE_ERROR);
return qs;
}
return qs;
}
/** /**
* Handle a "/reserve/withdraw" request. Parses the "reserve_pub" * Handle a "/reserve/withdraw" request. Parses the "reserve_pub"
* EdDSA key of the reserve and the requested "denom_pub" which * EdDSA key of the reserve and the requested "denom_pub" which
@ -52,29 +394,22 @@ TEH_RESERVE_handler_reserve_withdraw (struct TEH_RequestHandler *rh,
const char *upload_data, const char *upload_data,
size_t *upload_data_size) size_t *upload_data_size)
{ {
struct WithdrawContext wc;
json_t *root; json_t *root;
struct TALER_WithdrawRequestPS wsrd;
int res; int res;
struct TALER_DenominationPublicKey denomination_pub; int mhd_ret;
char *blinded_msg;
size_t blinded_msg_len;
struct TALER_Amount amount; struct TALER_Amount amount;
struct TALER_Amount amount_with_fee;
struct TALER_Amount fee_withdraw; struct TALER_Amount fee_withdraw;
struct TALER_ReserveSignatureP signature;
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
struct TEH_KS_StateHandle *ks;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_varsize ("coin_ev", GNUNET_JSON_spec_varsize ("coin_ev",
(void **) &blinded_msg, (void **) &wc.blinded_msg,
&blinded_msg_len), &wc.blinded_msg_len),
GNUNET_JSON_spec_fixed_auto ("reserve_pub", GNUNET_JSON_spec_fixed_auto ("reserve_pub",
&wsrd.reserve_pub), &wc.wsrd.reserve_pub),
GNUNET_JSON_spec_fixed_auto ("reserve_sig", GNUNET_JSON_spec_fixed_auto ("reserve_sig",
&signature), &wc.signature),
TALER_JSON_spec_denomination_public_key ("denom_pub", TALER_JSON_spec_denomination_public_key ("denom_pub",
&denomination_pub), &wc.denomination_pub),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
@ -93,60 +428,79 @@ TEH_RESERVE_handler_reserve_withdraw (struct TEH_RequestHandler *rh,
json_decref (root); json_decref (root);
if (GNUNET_OK != res) if (GNUNET_OK != res)
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
ks = TEH_KS_acquire (); wc.key_state = TEH_KS_acquire ();
dki = TEH_KS_denomination_key_lookup (ks, wc.dki = TEH_KS_denomination_key_lookup (wc.key_state,
&denomination_pub, &wc.denomination_pub,
TEH_KS_DKU_WITHDRAW); TEH_KS_DKU_WITHDRAW);
if (NULL == dki) if (NULL == wc.dki)
{ {
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (spec);
TEH_KS_release (ks); TEH_KS_release (wc.key_state);
return TEH_RESPONSE_reply_arg_unknown (connection, return TEH_RESPONSE_reply_arg_unknown (connection,
TALER_EC_WITHDRAW_DENOMINATION_KEY_NOT_FOUND, TALER_EC_WITHDRAW_DENOMINATION_KEY_NOT_FOUND,
"denom_pub"); "denom_pub");
} }
TALER_amount_ntoh (&amount, TALER_amount_ntoh (&amount,
&dki->issue.properties.value); &wc.dki->issue.properties.value);
TALER_amount_ntoh (&fee_withdraw, TALER_amount_ntoh (&fee_withdraw,
&dki->issue.properties.fee_withdraw); &wc.dki->issue.properties.fee_withdraw);
GNUNET_assert (GNUNET_OK == if (GNUNET_OK !=
TALER_amount_add (&amount_with_fee, TALER_amount_add (&wc.amount_required,
&amount, &amount,
&fee_withdraw)); &fee_withdraw))
TALER_amount_hton (&wsrd.amount_with_fee, {
&amount_with_fee); GNUNET_JSON_parse_free (spec);
TALER_amount_hton (&wsrd.withdraw_fee, TEH_KS_release (wc.key_state);
return TEH_RESPONSE_reply_internal_error (connection,
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); &fee_withdraw);
TEH_KS_release (ks);
/* verify signature! */ /* verify signature! */
wsrd.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS)); wc.wsrd.purpose.size
wsrd.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW); = htonl (sizeof (struct TALER_WithdrawRequestPS));
wc.wsrd.purpose.purpose
GNUNET_CRYPTO_rsa_public_key_hash (denomination_pub.rsa_public_key, = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
&wsrd.h_denomination_pub); GNUNET_CRYPTO_rsa_public_key_hash (wc.denomination_pub.rsa_public_key,
GNUNET_CRYPTO_hash (blinded_msg, &wc.wsrd.h_denomination_pub);
blinded_msg_len, GNUNET_CRYPTO_hash (wc.blinded_msg,
&wsrd.h_coin_envelope); wc.blinded_msg_len,
&wc.wsrd.h_coin_envelope);
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW, GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
&wsrd.purpose, &wc.wsrd.purpose,
&signature.eddsa_signature, &wc.signature.eddsa_signature,
&wsrd.reserve_pub.eddsa_pub)) &wc.wsrd.reserve_pub.eddsa_pub))
{ {
TALER_LOG_WARNING ("Client supplied invalid signature for /reserve/withdraw request\n"); TALER_LOG_WARNING ("Client supplied invalid signature for /reserve/withdraw request\n");
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (spec);
TEH_KS_release (wc.key_state);
return TEH_RESPONSE_reply_signature_invalid (connection, return TEH_RESPONSE_reply_signature_invalid (connection,
TALER_EC_WITHDRAW_RESERVE_SIGNATURE_INVALID, TALER_EC_WITHDRAW_RESERVE_SIGNATURE_INVALID,
"reserve_sig"); "reserve_sig");
} }
res = TEH_DB_execute_reserve_withdraw (connection,
&wsrd.reserve_pub, if (GNUNET_OK !=
&denomination_pub, TEH_DB_run_transaction (connection,
blinded_msg, &mhd_ret,
blinded_msg_len, &withdraw_transaction,
&signature); &wc))
{
TEH_KS_release (wc.key_state);
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (spec);
return res; return mhd_ret;
}
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;
} }
/* end of taler-exchange-httpd_reserve.c */
/* end of taler-exchange-httpd_reserve_withdraw.c */

View File

@ -685,8 +685,8 @@ TEH_RESPONSE_reply_coin_insufficient_funds (struct MHD_Connection *connection,
* @param[out] balance set to current reserve balance * @param[out] balance set to current reserve balance
* @return json representation of the @a rh, NULL on error * @return json representation of the @a rh, NULL on error
*/ */
static json_t * json_t *
compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHistory *rh, TEH_RESPONSE_compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHistory *rh,
struct TALER_Amount *balance) struct TALER_Amount *balance)
{ {
struct TALER_Amount deposit_total; struct TALER_Amount deposit_total;
@ -958,91 +958,6 @@ TEH_RESPONSE_reply_refund_success (struct MHD_Connection *connection,
} }
/**
* Send reserve status information to client.
*
* @param connection connection to the client
* @param rh reserve history to return
* @return MHD result code
*/
int
TEH_RESPONSE_reply_reserve_status_success (struct MHD_Connection *connection,
const struct TALER_EXCHANGEDB_ReserveHistory *rh)
{
json_t *json_balance;
json_t *json_history;
struct TALER_Amount balance;
json_history = compile_reserve_history (rh,
&balance);
if (NULL == json_history)
return TEH_RESPONSE_reply_internal_error (connection,
TALER_EC_RESERVE_STATUS_DB_ERROR,
"balance calculation failure");
json_balance = TALER_JSON_from_amount (&balance);
return TEH_RESPONSE_reply_json_pack (connection,
MHD_HTTP_OK,
"{s:o, s:o}",
"balance", json_balance,
"history", json_history);
}
/**
* Send reserve status information to client with the
* message that we have insufficient funds for the
* requested /reserve/withdraw operation.
*
* @param connection connection to the client
* @param rh reserve history to return
* @return MHD result code
*/
int
TEH_RESPONSE_reply_reserve_withdraw_insufficient_funds (struct MHD_Connection *connection,
const struct TALER_EXCHANGEDB_ReserveHistory *rh)
{
json_t *json_balance;
json_t *json_history;
struct TALER_Amount balance;
json_history = compile_reserve_history (rh,
&balance);
if (NULL == json_history)
return TEH_RESPONSE_reply_internal_error (connection,
TALER_EC_WITHDRAW_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS,
"balance calculation failure");
json_balance = TALER_JSON_from_amount (&balance);
return TEH_RESPONSE_reply_json_pack (connection,
MHD_HTTP_FORBIDDEN,
"{s:s, s:I, s:o, s:o}",
"error", "Insufficient funds",
"code", (json_int_t) TALER_EC_WITHDRAW_INSUFFICIENT_FUNDS,
"balance", json_balance,
"history", json_history);
}
/**
* Send blinded coin information to client.
*
* @param connection connection to the client
* @param collectable blinded coin to return
* @return MHD result code
*/
int
TEH_RESPONSE_reply_reserve_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);
return TEH_RESPONSE_reply_json_pack (connection,
MHD_HTTP_OK,
"{s:o}",
"ev_sig", sig_json);
}
/** /**
* Send a response for a failed "/refresh/melt" request. The * Send a response for a failed "/refresh/melt" request. The
* transaction history of the given coin demonstrates that the * transaction history of the given coin demonstrates that the

View File

@ -243,6 +243,19 @@ int
TEH_RESPONSE_reply_invalid_json (struct MHD_Connection *connectionx); TEH_RESPONSE_reply_invalid_json (struct MHD_Connection *connectionx);
/**
* Compile the history of a reserve into a JSON object
* and calculate the total balance.
*
* @param rh reserve history to JSON-ify
* @param[out] balance set to current reserve balance
* @return json representation of the @a rh, NULL on error
*/
json_t *
TEH_RESPONSE_compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHistory *rh,
struct TALER_Amount *balance);
/** /**
* Send proof that a request is invalid to client because of * Send proof that a request is invalid to client because of
* insufficient funds. This function will create a message with all * insufficient funds. This function will create a message with all
@ -411,44 +424,6 @@ TEH_RESPONSE_reply_track_transfer_details (struct MHD_Connection *connection,
const struct TEH_TrackTransferDetail *wdd_head); const struct TEH_TrackTransferDetail *wdd_head);
/**
* Send reserve status information to client.
*
* @param connection connection to the client
* @param rh reserve history to return
* @return MHD result code
*/
int
TEH_RESPONSE_reply_reserve_status_success (struct MHD_Connection *connection,
const struct TALER_EXCHANGEDB_ReserveHistory *rh);
/**
* Send reserve status information to client with the
* message that we have insufficient funds for the
* requested /reserve/withdraw operation.
*
* @param connection connection to the client
* @param rh reserve history to return
* @return MHD result code
*/
int
TEH_RESPONSE_reply_reserve_withdraw_insufficient_funds (struct MHD_Connection *connection,
const struct TALER_EXCHANGEDB_ReserveHistory *rh);
/**
* Send blinded coin information to client.
*
* @param connection connection to the client
* @param collectable blinded coin to return
* @return MHD result code
*/
int
TEH_RESPONSE_reply_reserve_withdraw_success (struct MHD_Connection *connection,
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable);
/** /**
* Send a confirmation response to a "/refresh/melt" request. * Send a confirmation response to a "/refresh/melt" request.
* *

View File

@ -1496,31 +1496,31 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state *state)
case PERF_TALER_EXCHANGEDB_CMD_INSERT_WITHDRAW: case PERF_TALER_EXCHANGEDB_CMD_INSERT_WITHDRAW:
{ {
unsigned int coin_index; unsigned int coin_index;
int ret; enum GNUNET_DB_QueryStatus qs;
struct PERF_TALER_EXCHANGEDB_Coin *coin; struct PERF_TALER_EXCHANGEDB_Coin *coin;
coin_index = state->cmd[state->i].details.insert_withdraw.index_coin; coin_index = state->cmd[state->i].details.insert_withdraw.index_coin;
coin = state->cmd[coin_index].exposed.data.coin; coin = state->cmd[coin_index].exposed.data.coin;
ret = state->plugin->insert_withdraw_info (state->plugin->cls, qs = state->plugin->insert_withdraw_info (state->plugin->cls,
state->session, state->session,
&coin->blind); &coin->blind);
GNUNET_assert (GNUNET_SYSERR != ret); GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
} }
break; break;
case PERF_TALER_EXCHANGEDB_CMD_GET_WITHDRAW: case PERF_TALER_EXCHANGEDB_CMD_GET_WITHDRAW:
{ {
unsigned int source_index; unsigned int source_index;
int ret; enum GNUNET_DB_QueryStatus qs;
struct PERF_TALER_EXCHANGEDB_Data *data; struct PERF_TALER_EXCHANGEDB_Data *data;
source_index = state->cmd[state->i].details.get_denomination.index_denom; source_index = state->cmd[state->i].details.get_denomination.index_denom;
data = &state->cmd[source_index].exposed; data = &state->cmd[source_index].exposed;
ret = state->plugin->get_withdraw_info (state->plugin->cls, qs = state->plugin->get_withdraw_info (state->plugin->cls,
state->session, state->session,
&data->data.coin->blind.h_coin_envelope, &data->data.coin->blind.h_coin_envelope,
&data->data.coin->blind); &data->data.coin->blind);
GNUNET_assert (GNUNET_SYSERR != ret); GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
} }
break; break;

View File

@ -2242,39 +2242,18 @@ postgres_get_latest_reserve_in_reference (void *cls,
* `h_coin_envelope` in the @a collectable to be returned) * `h_coin_envelope` in the @a collectable to be returned)
* @param collectable corresponding collectable coin (blind signature) * @param collectable corresponding collectable coin (blind signature)
* if a coin is found * if a coin is found
* @return #GNUNET_SYSERR on internal error * @return statement execution status
* #GNUNET_NO if the collectable was not found
* #GNUNET_YES on success
*/ */
static int static enum GNUNET_DB_QueryStatus
postgres_get_withdraw_info (void *cls, postgres_get_withdraw_info (void *cls,
struct TALER_EXCHANGEDB_Session *session, struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *h_blind, const struct GNUNET_HashCode *h_blind,
struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable) struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable)
{ {
PGresult *result;
struct GNUNET_PQ_QueryParam params[] = { struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (h_blind), GNUNET_PQ_query_param_auto_from_type (h_blind),
GNUNET_PQ_query_param_end GNUNET_PQ_query_param_end
}; };
int ret;
ret = GNUNET_SYSERR;
result = GNUNET_PQ_exec_prepared (session->conn,
"get_withdraw_info",
params);
if (PGRES_TUPLES_OK != PQresultStatus (result))
{
QUERY_ERR (result, session->conn);
goto cleanup;
}
if (0 == PQntuples (result))
{
ret = GNUNET_NO;
goto cleanup;
}
{
struct GNUNET_PQ_ResultSpec rs[] = { struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_rsa_public_key ("denom_pub", GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
&collectable->denom_pub.rsa_public_key), &collectable->denom_pub.rsa_public_key),
@ -2291,21 +2270,11 @@ postgres_get_withdraw_info (void *cls,
GNUNET_PQ_result_spec_end GNUNET_PQ_result_spec_end
}; };
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
0))
{
GNUNET_break (0);
goto cleanup;
}
}
collectable->h_coin_envelope = *h_blind; collectable->h_coin_envelope = *h_blind;
ret = GNUNET_YES; return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
"get_withdraw_info",
cleanup: params,
PQclear (result); rs);
return ret;
} }
@ -2317,17 +2286,14 @@ postgres_get_withdraw_info (void *cls,
* @param session database connection to use * @param session database connection to use
* @param collectable corresponding collectable coin (blind signature) * @param collectable corresponding collectable coin (blind signature)
* if a coin is found * if a coin is found
* @return #GNUNET_SYSERR on internal error * @return query execution status
* #GNUNET_NO if we failed but should retry the transaction
* #GNUNET_YES on success
*/ */
static int static enum GNUNET_DB_QueryStatus
postgres_insert_withdraw_info (void *cls, postgres_insert_withdraw_info (void *cls,
struct TALER_EXCHANGEDB_Session *session, struct TALER_EXCHANGEDB_Session *session,
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable) const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable)
{ {
struct PostgresClosure *pg = cls; struct PostgresClosure *pg = cls;
PGresult *result;
struct TALER_EXCHANGEDB_Reserve reserve; struct TALER_EXCHANGEDB_Reserve reserve;
struct GNUNET_HashCode denom_pub_hash; struct GNUNET_HashCode denom_pub_hash;
struct GNUNET_TIME_Absolute now; struct GNUNET_TIME_Absolute now;
@ -2347,28 +2313,27 @@ postgres_insert_withdraw_info (void *cls,
now = GNUNET_TIME_absolute_get (); now = GNUNET_TIME_absolute_get ();
GNUNET_CRYPTO_rsa_public_key_hash (collectable->denom_pub.rsa_public_key, GNUNET_CRYPTO_rsa_public_key_hash (collectable->denom_pub.rsa_public_key,
&denom_pub_hash); &denom_pub_hash);
result = GNUNET_PQ_exec_prepared (session->conn, qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
"insert_withdraw_info", "insert_withdraw_info",
params); params);
if (PGRES_COMMAND_OK != PQresultStatus (result)) if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{ {
QUERY_ERR (result, session->conn); GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
PQclear (result); return qs;
return GNUNET_SYSERR;
} }
PQclear (result);
/* update reserve balance */ /* update reserve balance */
reserve.pub = collectable->reserve_pub; reserve.pub = collectable->reserve_pub;
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
postgres_reserve_get (cls, (qs = postgres_reserve_get (cls,
session, session,
&reserve)) &reserve)))
{ {
/* FIXME: #5010 */
/* Should have been checked before we got here... */ /* Should have been checked before we got here... */
GNUNET_break (0); GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
return GNUNET_SYSERR; if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
qs = GNUNET_DB_STATUS_HARD_ERROR;
return qs;
} }
if (GNUNET_SYSERR == if (GNUNET_SYSERR ==
TALER_amount_subtract (&reserve.balance, TALER_amount_subtract (&reserve.balance,
@ -2381,7 +2346,7 @@ postgres_insert_withdraw_info (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Withdrawal from reserve `%s' refused due to balance missmatch. Retrying.\n", "Withdrawal from reserve `%s' refused due to balance missmatch. Retrying.\n",
TALER_B2S (&collectable->reserve_pub)); TALER_B2S (&collectable->reserve_pub));
return GNUNET_NO; return GNUNET_DB_STATUS_SOFT_ERROR;
} }
expiry = GNUNET_TIME_absolute_add (now, expiry = GNUNET_TIME_absolute_add (now,
pg->idle_reserve_expiration_time); pg->idle_reserve_expiration_time);
@ -2390,13 +2355,13 @@ postgres_insert_withdraw_info (void *cls,
qs = reserves_update (cls, qs = reserves_update (cls,
session, session,
&reserve); &reserve);
if (0 >= qs) GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{ {
GNUNET_break (0); GNUNET_break (0);
qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR;
} }
return GNUNET_OK; return qs;
} }

View File

@ -1584,7 +1584,7 @@ run (void *cls)
cbc.amount_with_fee = value; cbc.amount_with_fee = value;
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (CURRENCY, &cbc.withdraw_fee)); TALER_amount_get_zero (CURRENCY, &cbc.withdraw_fee));
FAILIF (GNUNET_OK != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_withdraw_info (plugin->cls, plugin->insert_withdraw_info (plugin->cls,
session, session,
&cbc)); &cbc));
@ -1604,7 +1604,7 @@ run (void *cls)
&reserve_pub2, &reserve_pub2,
sizeof (reserve_pub))); sizeof (reserve_pub)));
FAILIF (GNUNET_YES != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_withdraw_info (plugin->cls, plugin->get_withdraw_info (plugin->cls,
session, session,
&cbc.h_coin_envelope, &cbc.h_coin_envelope,

View File

@ -1232,11 +1232,9 @@ struct TALER_EXCHANGEDB_Plugin
* `h_coin_envelope` in the @a collectable to be returned) * `h_coin_envelope` in the @a collectable to be returned)
* @param collectable corresponding collectable coin (blind signature) * @param collectable corresponding collectable coin (blind signature)
* if a coin is found * if a coin is found
* @return #GNUNET_SYSERR on internal error * @return statement execution status
* #GNUNET_NO if the collectable was not found
* #GNUNET_YES on success
*/ */
int enum GNUNET_DB_QueryStatus
(*get_withdraw_info) (void *cls, (*get_withdraw_info) (void *cls,
struct TALER_EXCHANGEDB_Session *session, struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *h_blind, const struct GNUNET_HashCode *h_blind,
@ -1251,11 +1249,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param session database connection to use * @param session database connection to use
* @param collectable corresponding collectable coin (blind signature) * @param collectable corresponding collectable coin (blind signature)
* if a coin is found * if a coin is found
* @return #GNUNET_SYSERR on internal error * @return statement execution status
* #GNUNET_NO if the collectable was not found
* #GNUNET_YES on success
*/ */
int enum GNUNET_DB_QueryStatus
(*insert_withdraw_info) (void *cls, (*insert_withdraw_info) (void *cls,
struct TALER_EXCHANGEDB_Session *session, struct TALER_EXCHANGEDB_Session *session,
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable); const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable);