recoup logic review, very minor cleanup

This commit is contained in:
Christian Grothoff 2020-03-23 13:24:33 +01:00
parent 67cf9747a5
commit bab34d15fc
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2017 Taler Systems SA Copyright (C) 2017-2020 Taler Systems SA
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
@ -33,50 +33,6 @@
#include "taler-exchange-httpd_keystate.h" #include "taler-exchange-httpd_keystate.h"
/**
* A wallet asked for /recoup (refresh variant), return the successful
* response.
*
* @param connection connection to the client
* @param old_coin_pub public key of the old coin that will receive the recoup
* @return MHD result code
*/
static int
reply_recoup_refresh_success (struct MHD_Connection *connection,
const struct
TALER_CoinSpendPublicKeyP *old_coin_pub)
{
return TALER_MHD_reply_json_pack (connection,
MHD_HTTP_OK,
"{s:o, s:b}",
"old_coin_pub",
GNUNET_JSON_from_data_auto (
old_coin_pub),
"refreshed", 1);
}
/**
* A wallet asked for /recoup (withdraw variant), return the successful
* response.
*
* @param connection connection to the client
* @param reserve_pub public key of the reserve that will receive the recoup
* @return MHD result code
*/
static int
reply_recoup_success (struct MHD_Connection *connection,
const struct TALER_ReservePublicKeyP *reserve_pub)
{
return TALER_MHD_reply_json_pack (connection,
MHD_HTTP_OK,
"{s:o, s:b}",
"reserve_pub",
GNUNET_JSON_from_data_auto (reserve_pub),
"refreshed", 0);
}
/** /**
* Closure for #recoup_transaction. * Closure for #recoup_transaction.
*/ */
@ -107,6 +63,10 @@ struct RecoupContext
*/ */
const struct TALER_CoinSpendSignatureP *coin_sig; const struct TALER_CoinSpendSignatureP *coin_sig;
/**
* Where does the value of the recouped coin go? Which member
* of the union is valid depends on @e refreshed.
*/
union union
{ {
/** /**
@ -142,7 +102,7 @@ struct RecoupContext
/** /**
* Execute a "/recoup". The validity of the coin and signature have * Execute a "recoup". The validity of the coin and signature have
* already been checked. The database must now check that the coin is * already been checked. The database must now check that the coin is
* not (double) spent, and execute the transaction. * not (double) spent, and execute the transaction.
* *
@ -170,7 +130,7 @@ recoup_transaction (void *cls,
struct TALER_Amount spent; struct TALER_Amount spent;
struct TALER_Amount recouped; struct TALER_Amount recouped;
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
int existing_recoup_found = GNUNET_NO; int existing_recoup_found;
/* Check whether a recoup is allowed, and if so, to which /* Check whether a recoup is allowed, and if so, to which
reserve / account the money should go */ reserve / account the money should go */
@ -220,7 +180,7 @@ recoup_transaction (void *cls,
*mhd_ret = TALER_MHD_reply_with_error (connection, *mhd_ret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_NOT_FOUND, MHD_HTTP_NOT_FOUND,
TALER_EC_RECOUP_WITHDRAW_NOT_FOUND, TALER_EC_RECOUP_WITHDRAW_NOT_FOUND,
"blind coin unknown"); "envelope unknown");
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
} }
@ -238,7 +198,7 @@ recoup_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_RECOUP_DB_FETCH_FAILED, TALER_EC_RECOUP_DB_FETCH_FAILED,
"failed to fetch old coin transaction history"); "failed to fetch coin transaction history");
} }
return qs; return qs;
} }
@ -250,6 +210,7 @@ recoup_transaction (void *cls,
TALER_amount_get_zero (pc->value.currency, TALER_amount_get_zero (pc->value.currency,
&recouped)); &recouped));
/* Check if this coin has been recouped already at least once */ /* Check if this coin has been recouped already at least once */
existing_recoup_found = GNUNET_NO;
for (struct TALER_EXCHANGEDB_TransactionList *pos = tl; for (struct TALER_EXCHANGEDB_TransactionList *pos = tl;
NULL != pos; NULL != pos;
pos = pos->next) pos = pos->next)
@ -292,17 +253,20 @@ recoup_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_RECOUP_COIN_BALANCE_NEGATIVE, TALER_EC_RECOUP_COIN_BALANCE_NEGATIVE,
"calculated negative old coin balance"); "calculated negative coin balance");
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
} }
if ( (0 == pc->amount.fraction) && if ( (0 == pc->amount.fraction) &&
(0 == pc->amount.value) ) (0 == pc->amount.value) )
{ {
/* Recoup has no effect: coin fully spent! */
int ret; int ret;
TEH_plugin->rollback (TEH_plugin->cls, TEH_plugin->rollback (TEH_plugin->cls,
session); session);
if (GNUNET_NO == existing_recoup_found) if (GNUNET_NO == existing_recoup_found)
{ {
/* Refuse: insufficient funds for recoup */
*mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (connection, *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (connection,
TALER_EC_RECOUP_COIN_BALANCE_ZERO, TALER_EC_RECOUP_COIN_BALANCE_ZERO,
&pc->coin->coin_pub, &pc->coin->coin_pub,
@ -312,7 +276,7 @@ recoup_transaction (void *cls,
else else
{ {
/* We didn't add any new recoup transaction, but there was at least /* We didn't add any new recoup transaction, but there was at least
one recoup before, so we give a success response. */ one recoup before, so we give a success response (idempotency!) */
ret = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; ret = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
} }
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
@ -352,7 +316,7 @@ recoup_transaction (void *cls,
{ {
if (GNUNET_DB_STATUS_HARD_ERROR == qs) if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{ {
TALER_LOG_WARNING ("Failed to store /recoup information in database\n"); TALER_LOG_WARNING ("Failed to store recoup information in database\n");
*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_RECOUP_DB_PUT_FAILED, TALER_EC_RECOUP_DB_PUT_FAILED,
@ -369,7 +333,7 @@ recoup_transaction (void *cls,
* some basic sanity checks (especially that the signature on the * some basic sanity checks (especially that the signature on the
* request and coin is valid) and then execute the recoup operation. * request and coin is valid) and then execute the recoup operation.
* Note that we need the DB to check the fee structure, so this is not * Note that we need the DB to check the fee structure, so this is not
* done here. * done here but during the recoup_transaction().
* *
* @param connection the MHD connection to handle * @param connection the MHD connection to handle
* @param coin information about the coin * @param coin information about the coin
@ -388,7 +352,6 @@ verify_and_execute_recoup (struct MHD_Connection *connection,
{ {
struct RecoupContext pc; struct RecoupContext pc;
const struct TALER_EXCHANGEDB_DenominationKey *dki; const struct TALER_EXCHANGEDB_DenominationKey *dki;
struct TALER_RecoupRequestPS pr;
struct GNUNET_HashCode c_hash; struct GNUNET_HashCode c_hash;
void *coin_ev; void *coin_ev;
size_t coin_ev_size; size_t coin_ev_size;
@ -417,11 +380,11 @@ verify_and_execute_recoup (struct MHD_Connection *connection,
{ {
TEH_KS_release (key_state); TEH_KS_release (key_state);
TALER_LOG_WARNING ( TALER_LOG_WARNING (
"Denomination key in /recoup request not in recoup mode\n"); "Denomination key in recoup request not in recoup mode\n");
return TALER_MHD_reply_with_error (connection, return TALER_MHD_reply_with_error (connection,
hc, hc,
ec, ec,
"denomination not allowing recoup"); "denomination not valid for recoup");
} }
TALER_amount_ntoh (&pc.value, TALER_amount_ntoh (&pc.value,
&dki->issue.properties.value); &dki->issue.properties.value);
@ -431,7 +394,7 @@ verify_and_execute_recoup (struct MHD_Connection *connection,
TALER_test_coin_valid (coin, TALER_test_coin_valid (coin,
&dki->denom_pub)) &dki->denom_pub))
{ {
TALER_LOG_WARNING ("Invalid coin passed for /recoup\n"); TALER_LOG_WARNING ("Invalid coin passed for recoup\n");
TEH_KS_release (key_state); TEH_KS_release (key_state);
return TALER_MHD_reply_with_error (connection, return TALER_MHD_reply_with_error (connection,
MHD_HTTP_FORBIDDEN, MHD_HTTP_FORBIDDEN,
@ -440,24 +403,28 @@ verify_and_execute_recoup (struct MHD_Connection *connection,
} }
/* check recoup request signature */ /* check recoup request signature */
pr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP);
pr.purpose.size = htonl (sizeof (struct TALER_RecoupRequestPS));
pr.coin_pub = coin->coin_pub;
pr.h_denom_pub = dki->issue.properties.denom_hash;
pr.coin_blind = *coin_bks;
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_RECOUP,
&pr.purpose,
&coin_sig->eddsa_signature,
&coin->coin_pub.eddsa_pub))
{ {
TALER_LOG_WARNING ("Invalid signature on /recoup request\n"); struct TALER_RecoupRequestPS pr = {
TEH_KS_release (key_state); .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP),
return TALER_MHD_reply_with_error (connection, .purpose.size = htonl (sizeof (struct TALER_RecoupRequestPS)),
MHD_HTTP_FORBIDDEN, .coin_pub = coin->coin_pub,
TALER_EC_RECOUP_SIGNATURE_INVALID, .h_denom_pub = dki->issue.properties.denom_hash,
"coin_sig"); .coin_blind = *coin_bks
};
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_RECOUP,
&pr.purpose,
&coin_sig->eddsa_signature,
&coin->coin_pub.eddsa_pub))
{
TALER_LOG_WARNING ("Invalid signature on recoup request\n");
TEH_KS_release (key_state);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_FORBIDDEN,
TALER_EC_RECOUP_SIGNATURE_INVALID,
"coin_sig");
}
} }
GNUNET_CRYPTO_hash (&coin->coin_pub.eddsa_pub, GNUNET_CRYPTO_hash (&coin->coin_pub.eddsa_pub,
sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey), sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
@ -499,6 +466,7 @@ verify_and_execute_recoup (struct MHD_Connection *connection,
return mhd_ret; return mhd_ret;
} }
/* Perform actual recoup transaction */
pc.coin_sig = coin_sig; pc.coin_sig = coin_sig;
pc.coin_bks = coin_bks; pc.coin_bks = coin_bks;
pc.coin = coin; pc.coin = coin;
@ -514,11 +482,22 @@ verify_and_execute_recoup (struct MHD_Connection *connection,
&pc)) &pc))
return mhd_ret; return mhd_ret;
} }
/* Recoup succeeded, return result */
return (refreshed) return (refreshed)
? reply_recoup_refresh_success (connection, ? TALER_MHD_reply_json_pack (connection,
&pc.target.old_coin_pub) MHD_HTTP_OK,
: reply_recoup_success (connection, "{s:o, s:b}",
&pc.target.reserve_pub); "old_coin_pub",
GNUNET_JSON_from_data_auto (
&pc.target.old_coin_pub),
"refreshed", 1)
: TALER_MHD_reply_json_pack (connection,
MHD_HTTP_OK,
"{s:o, s:b}",
"reserve_pub",
GNUNET_JSON_from_data_auto (
&pc.target.reserve_pub),
"refreshed", 0);
} }