include fees in amounts being signed, check available balance on refresh

This commit is contained in:
Christian Grothoff 2015-03-22 16:09:01 +01:00
parent c2a42d5475
commit 1277f8445d
10 changed files with 183 additions and 111 deletions

View File

@ -131,6 +131,15 @@ struct TALER_WithdrawRequest
*/
struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub;
/**
* Value of the coin being minted (matching the denomination key)
* plus the transaction fee. We include this in what is being
* signed so that we can verify a reserve's remaining total balance
* without needing to access the respective denomination key
* information each time.
*/
struct TALER_AmountNBO amount_with_fee;
/**
* Hash of the denomination public key for the coin that is withdrawn.
*/
@ -171,9 +180,11 @@ struct TALER_DepositRequest
uint64_t transaction_id GNUNET_PACKED;
/**
* Amount to be deposited.
* Amount to be deposited, including fee.
*/
struct TALER_AmountNBO amount;
struct TALER_AmountNBO amount_with_fee;
/* FIXME: we should probably also include the value of
the depositing fee here as well! */
/**
* The coin's public key.
@ -211,9 +222,12 @@ struct TALER_DepositConfirmation
uint64_t transaction_id GNUNET_PACKED;
/**
* Amount to be deposited.
* Amount to be deposited, including fee.
*/
struct TALER_AmountNBO amount;
struct TALER_AmountNBO amount_with_fee;
/* FIXME: we should probably also include the value of
the depositing fee here as well! */
/**
* The coin's public key.
@ -245,11 +259,17 @@ struct RefreshMeltCoinSignature
struct GNUNET_HashCode melt_hash;
/**
* How much of the value of the coin should be melted?
* This amount includes the fees, so the final amount contributed
* to the melt is this value minus the fee for melting the coin.
* How much of the value of the coin should be melted? This amount
* includes the fees, so the final amount contributed to the melt is
* this value minus the fee for melting the coin. We include the
* fee in what is being signed so that we can verify a reserve's
* remaining total balance without needing to access the respective
* denomination key information each time.
*/
struct TALER_AmountNBO amount;
struct TALER_AmountNBO amount_with_fee;
/* FIXME: we should probably also include the value of
the melting fee here as well! */
/**
* The coin's public key.
@ -282,9 +302,13 @@ struct RefreshMeltSessionSignature
/**
* What is the total value of the coins created during the
* refresh, excluding fees?
* refresh, including melting fee!
*/
struct TALER_AmountNBO amount;
struct TALER_AmountNBO amount_with_fee;
/* FIXME: we should probably also include the value of
the melting fee here as well! */
};

View File

@ -1401,7 +1401,7 @@ postgres_insert_deposit (void *cls,
&denom_sig_enc);
json_wire_enc = json_dumps (deposit->wire, JSON_COMPACT);
TALER_amount_hton (&amount_nbo,
&deposit->amount);
&deposit->amount_with_fee);
struct TALER_DB_QueryParam params[]= {
TALER_DB_QUERY_PARAM_PTR (&deposit->coin.coin_pub),
TALER_DB_QUERY_PARAM_PTR_SIZED (denom_pub_enc, denom_pub_enc_size),

View File

@ -34,6 +34,64 @@
#include "plugin.h"
/**
* Calculate the total value of all transactions performed.
* Stores @a off plus the cost of all transactions in @a tl
* in @a ret.
*
* @param pos transaction list to process
* @param off offset to use as the starting value
* @param ret where the resulting total is to be stored
* @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
*/
static int
calculate_transaction_list_totals (struct TALER_MINT_DB_TransactionList *tl,
const struct TALER_Amount *off,
struct TALER_Amount *ret)
{
struct TALER_Amount spent = *off;
struct TALER_MINT_DB_TransactionList *pos;
for (pos = tl; NULL != pos; pos = pos->next)
{
switch (pos->type)
{
case TALER_MINT_DB_TT_DEPOSIT:
if (GNUNET_OK !=
TALER_amount_add (&spent,
&spent,
&pos->details.deposit->amount_with_fee))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
break;
case TALER_MINT_DB_TT_REFRESH_MELT:
if (GNUNET_OK !=
TALER_amount_add (&spent,
&spent,
&pos->details.melt->amount_with_fee))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
break;
case TALER_MINT_DB_TT_LOCK:
/* should check if lock is still active,
and if it is for THIS operation; if
lock is inactive, delete it; if lock
is for THIS operation, ignore it;
if lock is for another operation,
count it! */
GNUNET_assert (0); // FIXME: not implemented! (#3625)
return GNUNET_SYSERR;
}
}
*ret = spent;
return GNUNET_OK;
}
/**
* Execute a deposit. The validity of the coin and signature
* have already been checked. The database must now check that
@ -50,11 +108,9 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection,
{
struct TALER_MINTDB_Session *session;
struct TALER_MINT_DB_TransactionList *tl;
struct TALER_MINT_DB_TransactionList *pos;
struct TALER_Amount spent;
struct TALER_Amount value;
struct TALER_Amount fee_deposit;
struct TALER_Amount fee_refresh;
struct MintKeyState *mks;
struct TALER_MINT_DenomKeyIssuePriv *dki;
int ret;
@ -76,7 +132,7 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection,
&deposit->h_contract,
deposit->transaction_id,
&deposit->merchant_pub,
&deposit->amount);
&deposit->amount_with_fee);
}
mks = TALER_MINT_key_state_acquire ();
dki = TALER_MINT_get_denom_key (mks,
@ -85,8 +141,6 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection,
&dki->issue.value);
TALER_amount_ntoh (&fee_deposit,
&dki->issue.fee_deposit);
TALER_amount_ntoh (&fee_refresh,
&dki->issue.fee_refresh);
TALER_MINT_key_state_release (mks);
if (GNUNET_OK !=
@ -96,69 +150,29 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection,
GNUNET_break (0);
return TALER_MINT_reply_internal_db_error (connection);
}
/* fee for THIS transaction */
spent = deposit->amount_with_fee;
if (TALER_amount_cmp (&fee_deposit,
&spent) < 0)
{
return (MHD_YES ==
TALER_MINT_reply_external_error (connection,
"deposited amount smaller than depositing fee"))
? GNUNET_NO : GNUNET_SYSERR;
}
/* add cost of all previous transactions */
tl = plugin->get_coin_transactions (plugin->cls,
session,
&deposit->coin.coin_pub);
spent = fee_deposit; /* fee for THIS transaction */
if (GNUNET_OK !=
TALER_amount_add (&spent,
&spent,
&deposit->amount))
calculate_transaction_list_totals (tl,
&spent,
&spent))
{
GNUNET_break (0);
plugin->free_coin_transaction_list (plugin->cls,
tl);
return TALER_MINT_reply_internal_db_error (connection);
}
for (pos = tl; NULL != pos; pos = pos->next)
{
switch (pos->type)
{
case TALER_MINT_DB_TT_DEPOSIT:
if ( (GNUNET_OK !=
TALER_amount_add (&spent,
&spent,
&pos->details.deposit->amount)) ||
(GNUNET_OK !=
TALER_amount_add (&spent,
&spent,
&fee_deposit)) )
{
GNUNET_break (0);
plugin->free_coin_transaction_list (plugin->cls,
tl);
return TALER_MINT_reply_internal_db_error (connection);
}
break;
case TALER_MINT_DB_TT_REFRESH_MELT:
if ( (GNUNET_OK !=
TALER_amount_add (&spent,
&spent,
&pos->details.melt->amount)) ||
(GNUNET_OK !=
TALER_amount_add (&spent,
&spent,
&fee_refresh)) )
{
GNUNET_break (0);
plugin->free_coin_transaction_list (plugin->cls,
tl);
return TALER_MINT_reply_internal_db_error (connection);
}
break;
case TALER_MINT_DB_TT_LOCK:
/* should check if lock is still active,
and if it is for THIS operation; if
lock is inactive, delete it; if lock
is for THIS operation, ignore it;
if lock is for another operation,
count it! */
GNUNET_assert (0); // FIXME: not implemented! (#3625)
break;
}
}
if (0 < TALER_amount_cmp (&spent,
&value))
{
@ -197,7 +211,7 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection,
&deposit->h_contract,
deposit->transaction_id,
&deposit->merchant_pub,
&deposit->amount);
&deposit->amount_with_fee);
}
@ -501,8 +515,11 @@ refresh_accept_melts (struct MHD_Connection *connection,
{
struct TALER_MINT_DenomKeyIssue *dki;
struct TALER_MINT_DB_TransactionList *tl;
struct TALER_Amount fee_deposit;
struct TALER_Amount fee_refresh;
struct TALER_Amount coin_value;
struct TALER_Amount coin_residual;
struct TALER_Amount spent;
struct RefreshMelt melt;
int res;
@ -518,26 +535,51 @@ refresh_accept_melts (struct MHD_Connection *connection,
"denom not found"))
? GNUNET_NO : GNUNET_SYSERR;
TALER_amount_ntoh (&fee_deposit,
&dki->fee_deposit);
TALER_amount_ntoh (&fee_refresh,
&dki->fee_refresh);
TALER_amount_ntoh (&coin_value,
&dki->value);
/* fee for THIS transaction; the melt amount includes the fee! */
spent = coin_details->melt_amount_with_fee;
if (TALER_amount_cmp (&fee_refresh,
&spent) < 0)
{
return (MHD_YES ==
TALER_MINT_reply_external_error (connection,
"melt amount smaller than melting fee"))
? GNUNET_NO : GNUNET_SYSERR;
}
/* add historic transaction costs of this coin */
tl = plugin->get_coin_transactions (plugin->cls,
session,
&coin_public_info->coin_pub);
/* FIXME: #3636: compute how much value is left with this coin and
compare to `expected_value`! (subtract from "coin_value") */
coin_residual = coin_value;
if (GNUNET_OK !=
calculate_transaction_list_totals (tl,
&spent,
&spent))
{
GNUNET_break (0);
plugin->free_coin_transaction_list (plugin->cls,
tl);
return TALER_MINT_reply_internal_db_error (connection);
}
/* Refuse to refresh when the coin does not have enough money left to
* pay the refreshing fees of the coin. */
if (TALER_amount_cmp (&coin_residual,
&coin_details->melt_amount) < 0)
if (TALER_amount_cmp (&coin_value,
&spent) < 0)
{
GNUNET_assert (GNUNET_OK ==
TALER_amount_subtract (&coin_residual,
&spent,
&coin_details->melt_amount_with_fee));
res = (MHD_YES ==
TALER_MINT_reply_refresh_melt_insufficient_funds (connection,
&coin_public_info->coin_pub,
coin_value,
tl,
coin_details->melt_amount,
coin_details->melt_amount_with_fee,
coin_residual))
? GNUNET_NO : GNUNET_SYSERR;
plugin->free_coin_transaction_list (plugin->cls,
@ -550,7 +592,7 @@ refresh_accept_melts (struct MHD_Connection *connection,
melt.coin = *coin_public_info;
melt.coin_sig = coin_details->melt_sig;
melt.melt_hash = *melt_hash;
melt.amount = coin_details->melt_amount;
melt.amount_with_fee = coin_details->melt_amount_with_fee;
if (GNUNET_OK !=
plugin->insert_refresh_melt (plugin->cls,
session,

View File

@ -95,7 +95,7 @@ struct MeltDetails
* This amount includes the fees, so the final amount contributed
* to the melt is this value minus the fee for melting the coin.
*/
struct TALER_Amount melt_amount;
struct TALER_Amount melt_amount_with_fee;
};

View File

@ -65,8 +65,8 @@ verify_and_execute_deposit (struct MHD_Connection *connection,
dr.h_contract = deposit->h_contract;
dr.h_wire = deposit->h_wire;
dr.transaction_id = GNUNET_htonll (deposit->transaction_id);
TALER_amount_hton (&dr.amount,
&deposit->amount);
TALER_amount_hton (&dr.amount_with_fee,
&deposit->amount_with_fee);
dr.coin_pub = deposit->coin.coin_pub;
if (GNUNET_OK !=
GNUNET_CRYPTO_ecdsa_verify (TALER_SIGNATURE_WALLET_DEPOSIT,
@ -167,7 +167,7 @@ parse_and_handle_deposit_request (struct MHD_Connection *connection,
GNUNET_free (wire_enc);
deposit.wire = wire;
deposit.amount = *amount;
deposit.amount_with_fee = *amount;
res = verify_and_execute_deposit (connection,
&deposit);
TALER_MINT_release_parsed_data (spec);

View File

@ -109,8 +109,8 @@ handle_refresh_melt_binary (struct MHD_Connection *connection,
body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_SESSION);
body.purpose.size = htonl (sizeof (struct RefreshMeltSessionSignature));
body.melt_hash = melt_hash;
TALER_amount_hton (&body.amount,
&coin_melt_details->melt_amount);
TALER_amount_hton (&body.amount_with_fee,
&coin_melt_details->melt_amount_with_fee);
if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_REFRESH_MELT_SESSION,
&body.purpose,
@ -252,7 +252,7 @@ get_coin_public_info (struct MHD_Connection *connection,
? GNUNET_NO : GNUNET_SYSERR;
}
r_melt_detail->melt_sig = melt_sig;
r_melt_detail->melt_amount = amount;
r_melt_detail->melt_amount_with_fee = amount;
TALER_MINT_release_parsed_data (spec);
return GNUNET_OK;
}
@ -288,8 +288,8 @@ verify_coin_public_info (struct MHD_Connection *connection,
body.purpose.size = htonl (sizeof (struct RefreshMeltCoinSignature));
body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_COIN);
body.melt_hash = *melt_hash;
TALER_amount_hton (&body.amount,
&r_melt_detail->melt_amount);
TALER_amount_hton (&body.amount_with_fee,
&r_melt_detail->melt_amount_with_fee);
body.coin_pub = r_public_info->coin_pub;
if (GNUNET_OK !=
GNUNET_CRYPTO_ecdsa_verify (TALER_SIGNATURE_REFRESH_MELT_COIN,

View File

@ -299,7 +299,7 @@ TALER_MINT_reply_deposit_success (struct MHD_Connection *connection,
dc.h_contract = *h_contract;
dc.h_wire = *h_wire;
dc.transaction_id = GNUNET_htonll (transaction_id);
TALER_amount_hton (&dc.amount,
TALER_amount_hton (&dc.amount_with_fee,
amount);
dc.coin_pub = *coin_pub;
dc.merchant = *merchant;
@ -341,14 +341,14 @@ compile_transaction_history (const struct TALER_MINT_DB_TransactionList *tl)
const struct Deposit *deposit = pos->details.deposit;
type = "deposit";
value = deposit->amount;
value = deposit->amount_with_fee;
dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_DEPOSIT);
dr.purpose.size = htonl (sizeof (struct TALER_DepositRequest));
dr.h_contract = deposit->h_contract;
dr.h_wire = deposit->h_wire;
dr.transaction_id = GNUNET_htonll (deposit->transaction_id);
TALER_amount_hton (&dr.amount,
&deposit->amount);
TALER_amount_hton (&dr.amount_with_fee,
&deposit->amount_with_fee);
dr.coin_pub = deposit->coin.coin_pub;
transaction = TALER_JSON_from_ecdsa_sig (&dr.purpose,
&deposit->csig);
@ -360,12 +360,12 @@ compile_transaction_history (const struct TALER_MINT_DB_TransactionList *tl)
const struct RefreshMelt *melt = pos->details.melt;
type = "melt";
value = melt->amount;
value = melt->amount_with_fee;
ms.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_COIN);
ms.purpose.size = htonl (sizeof (struct RefreshMeltCoinSignature));
ms.melt_hash = melt->melt_hash;
TALER_amount_hton (&ms.amount,
&melt->amount);
TALER_amount_hton (&ms.amount_with_fee,
&melt->amount_with_fee);
ms.coin_pub = melt->coin.coin_pub;
transaction = TALER_JSON_from_ecdsa_sig (&ms.purpose,
&melt->coin_sig);

View File

@ -14,13 +14,13 @@
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file mint/mint_db.h
* @file mint/taler_mintdb_plugin.h
* @brief Low-level (statement-level) database access for the mint
* @author Florian Dold
* @author Christian Grothoff
*/
#ifndef MINT_DB_H
#define MINT_DB_H
#ifndef TALER_MINTDB_PLUGIN_H
#define TALER_MINTDB_PLUGIN_H
#include <gnunet/gnunet_util_lib.h>
#include "taler_util.h"
@ -87,6 +87,9 @@ struct CollectableBlindcoin
/**
* Denomination key (which coin was generated).
* FIXME: we should probably instead have the
* AMOUNT *including* fee in what is being signed
* as well!
*/
struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub;
@ -217,10 +220,10 @@ struct Deposit
uint64_t transaction_id;
/**
* Fraction of the coin's remaining value to be deposited.
* The coin is identified by @e coin_pub.
* Fraction of the coin's remaining value to be deposited, including
* depositing fee (if any). The coin is identified by @e coin_pub.
*/
struct TALER_Amount amount;
struct TALER_Amount amount_with_fee;
};
@ -296,11 +299,14 @@ struct RefreshMelt
struct GNUNET_HashCode melt_hash;
/**
* How much value is being melted?
* This amount includes the fees, so the final amount contributed
* to the melt is this value minus the fee for melting the coin.
* How much value is being melted? This amount includes the fees,
* so the final amount contributed to the melt is this value minus
* the fee for melting the coin. We include the fee in what is
* being signed so that we can verify a reserve's remaining total
* balance without needing to access the respective denomination key
* information each time.
*/
struct TALER_Amount amount;
struct TALER_Amount amount_with_fee;
};
@ -397,7 +403,7 @@ struct Lock
const struct GNUNET_CRYPTO_EcdsaSignature coin_sig;
/**
* How much value is being melted?
* How much value is being locked?
*/
struct TALER_Amount amount;

View File

@ -286,7 +286,7 @@ run (void *cls,
deposit.wire = wire;
deposit.transaction_id =
GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
deposit.amount = amount;
deposit.amount_with_fee = amount;
FAILIF (GNUNET_OK !=
plugin->insert_deposit (plugin->cls,
session, &deposit));

View File

@ -94,12 +94,12 @@ run (void *cls,
UINT64_MAX);
deposit->transaction_id = GNUNET_htonll (transaction_id);
/* Random amount */
deposit->amount.value =
deposit->amount_with_fee.value =
htonl (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX));
deposit->amount.fraction =
deposit->amount_with_fee.fraction =
htonl (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX));
GNUNET_assert (strlen (MINT_CURRENCY) < sizeof (deposit->amount.currency));
strcpy (deposit->amount.currency, MINT_CURRENCY);
GNUNET_assert (strlen (MINT_CURRENCY) < sizeof (deposit->amount_with_fee.currency));
strcpy (deposit->amount_with_fee.currency, MINT_CURRENCY);
/* Copy wireformat */
deposit->wire = json_loads (wire, 0, NULL);
EXITIF (GNUNET_OK !=