clean up /withdraw/sign logic

This commit is contained in:
Christian Grothoff 2015-01-29 00:45:07 +01:00
parent 8adde040ab
commit 758ce80519
3 changed files with 191 additions and 71 deletions

View File

@ -22,11 +22,6 @@
* - actually abstract DB implementation (i.e. via plugin logic) * - actually abstract DB implementation (i.e. via plugin logic)
* (this file should remain largely unchanged with the exception * (this file should remain largely unchanged with the exception
* of the PQ-specific DB handle types) * of the PQ-specific DB handle types)
* - /withdraw/sign: all
* + properly check all conditions and handle errors
* + properly check transaction logic
* + check for leaks
* + check low-level API
* - /refresh/melt: all * - /refresh/melt: all
* + properly check all conditions and handle errors * + properly check all conditions and handle errors
* + properly check transaction logic * + properly check transaction logic
@ -264,11 +259,17 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,
{ {
PGconn *db_conn; PGconn *db_conn;
struct ReserveHistory *rh; struct ReserveHistory *rh;
const struct ReserveHistory *pos;
struct MintKeyState *key_state; struct MintKeyState *key_state;
struct CollectableBlindcoin collectable; struct CollectableBlindcoin collectable;
struct TALER_MINT_DenomKeyIssuePriv *dki; struct TALER_MINT_DenomKeyIssuePriv *dki;
struct TALER_MINT_DenomKeyIssuePriv *tdki;
struct GNUNET_CRYPTO_rsa_Signature *sig; struct GNUNET_CRYPTO_rsa_Signature *sig;
struct TALER_Amount amount_required; struct TALER_Amount amount_required;
struct TALER_Amount deposit_total;
struct TALER_Amount withdraw_total;
struct TALER_Amount balance;
struct TALER_Amount value;
struct GNUNET_HashCode h_blind; struct GNUNET_HashCode h_blind;
int res; int res;
@ -299,63 +300,102 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,
return res; return res;
} }
GNUNET_assert (GNUNET_NO == res); GNUNET_assert (GNUNET_NO == res);
rh = TALER_MINT_DB_get_reserve_history (db_conn,
reserve);
if (NULL == rh)
return TALER_MINT_reply_json_pack (connection,
MHD_HTTP_NOT_FOUND,
"{s:s}",
"error",
"Reserve not found");
/* Check if balance is sufficient */
key_state = TALER_MINT_key_state_acquire (); key_state = TALER_MINT_key_state_acquire ();
dki = TALER_MINT_get_denom_key (key_state, dki = TALER_MINT_get_denom_key (key_state,
denomination_pub); denomination_pub);
TALER_MINT_key_state_release (key_state);
if (NULL == dki) if (NULL == dki)
{
TALER_MINT_key_state_release (key_state);
return TALER_MINT_reply_json_pack (connection, return TALER_MINT_reply_json_pack (connection,
MHD_HTTP_NOT_FOUND, MHD_HTTP_NOT_FOUND,
"{s:s}", "{s:s}",
"error", "error",
"Denomination not found"); "Denomination not found");
}
if (GNUNET_OK !=
TALER_MINT_DB_transaction (db_conn))
{
GNUNET_break (0);
TALER_MINT_key_state_release (key_state);
return TALER_MINT_reply_internal_db_error (connection);
}
amount_required = TALER_amount_add (TALER_amount_ntoh (dki->issue.value), rh = TALER_MINT_DB_get_reserve_history (db_conn,
TALER_amount_ntoh (dki->issue.fee_withdraw)); reserve);
// FIX LOGIC! if (NULL == rh)
#if 0 {
if (0 < TALER_amount_cmp (amount_required, TALER_MINT_DB_rollback (db_conn);
TALER_amount_ntoh (db_reserve.balance))) TALER_MINT_key_state_release (key_state);
return TALER_MINT_reply_json_pack (connection, return TALER_MINT_reply_json_pack (connection,
MHD_HTTP_PAYMENT_REQUIRED, MHD_HTTP_NOT_FOUND,
"{s:s}", "{s:s}",
"error", "error",
"Insufficient funds"); "Reserve not found");
}
db_reserve.balance = TALER_amount_hton /* calculate amount required including fees */
(TALER_amount_subtract (TALER_amount_ntoh (db_reserve.balance), amount_required = TALER_amount_add (TALER_amount_ntoh (dki->issue.value),
amount_required)); TALER_amount_ntoh (dki->issue.fee_withdraw));
/* calculate balance of the reserve */
res = 0;
for (pos = rh; NULL != pos; pos = pos->next)
{
switch (pos->type)
{
case TALER_MINT_DB_RO_BANK_TO_MINT:
if (0 == (res & 1))
deposit_total = pos->details.bank->amount;
else
deposit_total = TALER_amount_add (deposit_total,
pos->details.bank->amount);
res |= 1;
break;
case TALER_MINT_DB_RO_WITHDRAW_COIN:
tdki = TALER_MINT_get_denom_key (key_state,
pos->details.withdraw->denom_pub);
value = TALER_amount_ntoh (tdki->issue.value);
if (0 == (res & 2))
withdraw_total = value;
else
withdraw_total = TALER_amount_add (withdraw_total,
value);
res |= 2;
break;
}
}
/* FIXME: good place to assert deposit_total > withdraw_total... */
balance = TALER_amount_subtract (deposit_total,
withdraw_total);
if (0 < TALER_amount_cmp (amount_required,
balance))
{
TALER_MINT_key_state_release (key_state);
TALER_MINT_DB_rollback (db_conn);
res = TALER_MINT_reply_withdraw_sign_insufficient_funds (connection,
rh);
TALER_MINT_DB_free_reserve_history (rh);
return res;
}
TALER_MINT_DB_free_reserve_history (rh);
/* Balance is good, sign the coin! */
sig = GNUNET_CRYPTO_rsa_sign (dki->denom_priv, sig = GNUNET_CRYPTO_rsa_sign (dki->denom_priv,
blinded_msg, blinded_msg,
blinded_msg_len); blinded_msg_len);
TALER_MINT_key_state_release (key_state);
if (NULL == sig) if (NULL == sig)
{ {
GNUNET_break (0); GNUNET_break (0);
TALER_MINT_DB_rollback (db_conn);
return TALER_MINT_reply_internal_error (connection, return TALER_MINT_reply_internal_error (connection,
"Internal error"); "Internal error");
} }
/* transaction start */ // FIXME: can we avoid the cast?
if (GNUNET_OK !=
TALER_MINT_DB_update_reserve (db_conn,
&db_reserve,
GNUNET_YES))
{
GNUNET_break (0);
return TALER_MINT_reply_internal_db_error (connection);
}
#endif
collectable.denom_pub = (struct GNUNET_CRYPTO_rsa_PublicKey *) denomination_pub; collectable.denom_pub = (struct GNUNET_CRYPTO_rsa_PublicKey *) denomination_pub;
collectable.sig = sig; collectable.sig = sig;
collectable.reserve_pub = *reserve; collectable.reserve_pub = *reserve;
@ -367,17 +407,27 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,
{ {
GNUNET_break (0); GNUNET_break (0);
GNUNET_CRYPTO_rsa_signature_free (sig); GNUNET_CRYPTO_rsa_signature_free (sig);
TALER_MINT_DB_rollback (db_conn);
return TALER_MINT_reply_internal_db_error (connection); return TALER_MINT_reply_internal_db_error (connection);
} }
/* transaction end */ if (GNUNET_OK !=
GNUNET_CRYPTO_rsa_signature_free (sig); TALER_MINT_DB_commit (db_conn))
return TALER_MINT_reply_withdraw_sign_success (connection, {
&collectable); LOG_WARNING ("/withdraw/sign transaction commit failed\n");
return TALER_MINT_reply_commit_error (connection);
} }
res = TALER_MINT_reply_withdraw_sign_success (connection,
&collectable);
GNUNET_CRYPTO_rsa_signature_free (sig);
return res;
}
/** /**
* Insert all requested denominations into the db, and compute the * Insert all requested denominations into the DB, and compute the
* required cost of the denominations, including fees. * required cost of the denominations, including fees.
* *
* @param connection the connection to send an error response to * @param connection the connection to send an error response to
@ -615,11 +665,11 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection,
} }
if (GNUNET_OK != TALER_MINT_DB_transaction (db_conn)) if (GNUNET_OK !=
TALER_MINT_DB_transaction (db_conn))
{ {
// FIXME: return 'internal error'?
GNUNET_break (0); GNUNET_break (0);
return MHD_NO; return TALER_MINT_reply_internal_db_error (connection);
} }
if (GNUNET_OK != TALER_MINT_DB_create_refresh_session (db_conn, if (GNUNET_OK != TALER_MINT_DB_create_refresh_session (db_conn,
@ -678,10 +728,11 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection,
"not enough coins melted"); "not enough coins melted");
} }
if (GNUNET_OK != TALER_MINT_DB_commit (db_conn)) if (GNUNET_OK !=
TALER_MINT_DB_commit (db_conn))
{ {
GNUNET_break (0); LOG_WARNING ("/refresh/melt transaction commit failed\n");
return MHD_NO; return TALER_MINT_reply_commit_error (connection);
} }
if (GNUNET_OK != if (GNUNET_OK !=
(res = TALER_MINT_DB_get_refresh_session (db_conn, (res = TALER_MINT_DB_get_refresh_session (db_conn,
@ -795,11 +846,11 @@ TALER_MINT_db_execute_refresh_commit (struct MHD_Connection *connection,
return MHD_NO; return MHD_NO;
} }
if (GNUNET_OK != TALER_MINT_DB_transaction (db_conn)) if (GNUNET_OK !=
TALER_MINT_DB_transaction (db_conn))
{ {
// FIXME: return 'internal error'?
GNUNET_break (0); GNUNET_break (0);
return MHD_NO; return TALER_MINT_reply_internal_db_error (connection);
} }
/* Re-fetch the session information from the database, /* Re-fetch the session information from the database,
@ -816,11 +867,11 @@ TALER_MINT_db_execute_refresh_commit (struct MHD_Connection *connection,
return MHD_NO; return MHD_NO;
} }
if (GNUNET_OK != TALER_MINT_DB_commit (db_conn)) if (GNUNET_OK !=
TALER_MINT_DB_commit (db_conn))
{ {
// FIXME: return 'internal error'? LOG_WARNING ("/refresh/commit transaction commit failed\n");
GNUNET_break (0); return TALER_MINT_reply_commit_error (connection);
return MHD_NO;
} }
return TALER_MINT_reply_refresh_commit_success (connection, &refresh_session); return TALER_MINT_reply_refresh_commit_success (connection, &refresh_session);
@ -1095,11 +1146,11 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection,
} }
if (GNUNET_OK != TALER_MINT_DB_transaction (db_conn)) if (GNUNET_OK !=
TALER_MINT_DB_transaction (db_conn))
{ {
GNUNET_break (0); GNUNET_break (0);
// FIXME: return error code! return TALER_MINT_reply_internal_db_error (connection);
return MHD_NO;
} }
for (j = 0; j < refresh_session.num_newcoins; j++) for (j = 0; j < refresh_session.num_newcoins; j++)
@ -1169,10 +1220,11 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection,
return MHD_NO; return MHD_NO;
} }
if (GNUNET_OK != TALER_MINT_DB_commit (db_conn)) if (GNUNET_OK !=
TALER_MINT_DB_commit (db_conn))
{ {
GNUNET_break (0); LOG_WARNING ("/refresh/reveal transaction commit failed\n");
return MHD_NO; return TALER_MINT_reply_commit_error (connection);
} }
return helper_refresh_reveal_send_response (connection, return helper_refresh_reveal_send_response (connection,

View File

@ -351,26 +351,25 @@ TALER_MINT_reply_insufficient_funds (struct MHD_Connection *connection,
/** /**
* Send reserve status information to client. * Compile the history of a reserve into a JSON object
* and calculate the total balance.
* *
* @param connection connection to the client * @param rh reserve history to JSON-ify
* @param rh reserve history to return * @param balance[OUT] set to current reserve balance
* @return MHD result code * @return json representation of the @a rh
*/ */
int static json_t *
TALER_MINT_reply_withdraw_status_success (struct MHD_Connection *connection, compile_reserve_history (const struct ReserveHistory *rh,
const struct ReserveHistory *rh) struct TALER_Amount *balance)
{ {
struct TALER_Amount deposit_total; struct TALER_Amount deposit_total;
struct TALER_Amount withdraw_total; struct TALER_Amount withdraw_total;
struct TALER_Amount balance;
struct TALER_Amount value; struct TALER_Amount value;
json_t *json_balance;
json_t *json_history; json_t *json_history;
int ret; int ret;
struct MintKeyState *key_state;
const struct ReserveHistory *pos; const struct ReserveHistory *pos;
struct TALER_MINT_DenomKeyIssuePriv *dki; struct TALER_MINT_DenomKeyIssuePriv *dki;
struct MintKeyState *key_state;
json_history = json_array (); json_history = json_array ();
ret = 0; ret = 0;
@ -425,8 +424,30 @@ TALER_MINT_reply_withdraw_status_success (struct MHD_Connection *connection,
} }
TALER_MINT_key_state_release (key_state); TALER_MINT_key_state_release (key_state);
balance = TALER_amount_subtract (deposit_total, *balance = TALER_amount_subtract (deposit_total,
withdraw_total); withdraw_total);
return json_history;
}
/**
* Send reserve status information to client.
*
* @param connection connection to the client
* @param rh reserve history to return
* @return MHD result code
*/
int
TALER_MINT_reply_withdraw_status_success (struct MHD_Connection *connection,
const struct ReserveHistory *rh)
{
json_t *json_balance;
json_t *json_history;
struct TALER_Amount balance;
int ret;
json_history = compile_reserve_history (rh,
&balance);
json_balance = TALER_JSON_from_amount (balance); json_balance = TALER_JSON_from_amount (balance);
ret = TALER_MINT_reply_json_pack (connection, ret = TALER_MINT_reply_json_pack (connection,
MHD_HTTP_OK, MHD_HTTP_OK,
@ -439,6 +460,39 @@ TALER_MINT_reply_withdraw_status_success (struct MHD_Connection *connection,
} }
/**
* Send reserve status information to client with the
* message that we have insufficient funds for the
* requested /withdraw/sign operation.
*
* @param connection connection to the client
* @param rh reserve history to return
* @return MHD result code
*/
int
TALER_MINT_reply_withdraw_sign_insufficient_funds (struct MHD_Connection *connection,
const struct ReserveHistory *rh)
{
json_t *json_balance;
json_t *json_history;
struct TALER_Amount balance;
int ret;
json_history = compile_reserve_history (rh,
&balance);
json_balance = TALER_JSON_from_amount (balance);
ret = TALER_MINT_reply_json_pack (connection,
MHD_HTTP_PAYMENT_REQUIRED,
"{s:s, s:o, s:o}",
"error", "Insufficient funds"
"balance", json_balance,
"history", json_history);
json_decref (json_history);
json_decref (json_balance);
return ret;
}
/** /**
* Send blinded coin information to client. * Send blinded coin information to client.
* *

View File

@ -208,6 +208,20 @@ TALER_MINT_reply_withdraw_status_success (struct MHD_Connection *connection,
const struct ReserveHistory *rh); const struct ReserveHistory *rh);
/**
* Send reserve status information to client with the
* message that we have insufficient funds for the
* requested /withdraw/sign operation.
*
* @param connection connection to the client
* @param rh reserve history to return
* @return MHD result code
*/
int
TALER_MINT_reply_withdraw_sign_insufficient_funds (struct MHD_Connection *connection,
const struct ReserveHistory *rh);
/** /**
* Send blinded coin information to client. * Send blinded coin information to client.
* *