implement check_transaction_history()

This commit is contained in:
Christian Grothoff 2017-03-19 06:50:08 +01:00
parent 9b4d0634e1
commit 424b327395
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC

View File

@ -23,9 +23,6 @@
* the wire transfers from the bank. This needs to be checked separately!
* - Similarly, we do not check that the outgoing wire transfers match those
* given in the 'wire_out' table. This needs to be checked separately!
*
* TODO:
* - FIXME: do proper transaction history check in #check_transaction_history()
*/
#include "platform.h"
#include <gnunet/gnunet_util_lib.h>
@ -2002,16 +1999,17 @@ struct WireCheckContext
/**
* Check coin's transaction history for plausibility. Does NOT check
* the signatures (those are checked independently), but does check
* that the amounts add up to the picture claimed by the aggregation table.
* the signatures (those are checked independently), but does calculate
* the amounts for the aggregation table and checks that the total
* claimed coin value is within the value of the coin's denomination.
*
* @param coin_pub public key of the coin (for reporting)
* @param h_proposal_data hash of the proposal for which we calculate the amount
* @param merchant_pub public key of the merchant (who is allowed to issue refunds)
* @param dki denomination information about the coin
* @param tl_head head of transaction history to verify
* @param[out] final amount the coin contributes to the transaction
* @param[out] final fees the exchange charged for the transaction
* @param[out] merchant_gain amount the coin contributes to the wire transfer to the merchant
* @param[out] merchant_fees fees the exchange charged the merchant for the transaction(s)
* @return #GNUNET_OK on success, #GNUNET_SYSERR on error
*/
static int
@ -2020,12 +2018,14 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki,
const struct TALER_EXCHANGEDB_TransactionList *tl_head,
struct TALER_Amount *final_expenditures,
struct TALER_Amount *final_fees)
struct TALER_Amount *merchant_gain,
struct TALER_Amount *merchant_fees)
{
struct TALER_Amount expenditures;
struct TALER_Amount refunds;
struct TALER_Amount fees;
struct TALER_Amount spent;
struct TALER_Amount value;
struct TALER_Amount merchant_loss;
GNUNET_assert (NULL != tl_head);
TALER_amount_get_zero (currency,
@ -2033,16 +2033,22 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
TALER_amount_get_zero (currency,
&refunds);
TALER_amount_get_zero (currency,
&fees);
merchant_gain);
TALER_amount_get_zero (currency,
merchant_fees);
TALER_amount_get_zero (currency,
&merchant_loss);
/* Go over transaction history to compute totals; note that we do not
know the order, so instead of subtracting we compute positive
(deposit, melt) and negative (refund) values separately here,
and then subtract the negative from the positive after the loop. */
for (const struct TALER_EXCHANGEDB_TransactionList *tl = tl_head;NULL != tl;tl = tl->next)
{
const struct TALER_Amount *amount_with_fee;
const struct TALER_Amount *fee;
const struct TALER_AmountNBO *fee_dki;
struct TALER_Amount *add_to;
struct TALER_Amount tmp;
add_to = NULL;
// FIXME:
// - for refunds/deposits that apply to this merchant and this contract
// we need to update the total expenditures/refunds/fees
@ -2053,80 +2059,167 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
amount_with_fee = &tl->details.deposit->amount_with_fee;
fee = &tl->details.deposit->deposit_fee;
fee_dki = &dki->properties.fee_deposit;
add_to = &expenditures;
if (GNUNET_OK !=
TALER_amount_add (&expenditures,
&expenditures,
amount_with_fee))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
/* Check if this deposit is within the remit of the aggregation
we are investigating, if so, include it in the totals. */
if ( (0 == memcmp (merchant_pub,
&tl->details.deposit->merchant_pub,
sizeof (struct TALER_MerchantPublicKeyP))) &&
(0 == memcmp (h_proposal_data,
&tl->details.deposit->h_proposal_data,
sizeof (struct GNUNET_HashCode))) )
{
struct TALER_Amount amount_without_fee;
if (GNUNET_OK !=
TALER_amount_subtract (&amount_without_fee,
amount_with_fee,
fee))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
TALER_amount_add (merchant_gain,
merchant_gain,
&amount_without_fee))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
TALER_amount_add (merchant_fees,
merchant_fees,
fee))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
}
break;
case TALER_EXCHANGEDB_TT_REFRESH_MELT:
amount_with_fee = &tl->details.melt->amount_with_fee;
fee = &tl->details.melt->melt_fee;
fee_dki = &dki->properties.fee_refresh;
add_to = &expenditures;
if (GNUNET_OK !=
TALER_amount_add (&expenditures,
&expenditures,
amount_with_fee))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
break;
case TALER_EXCHANGEDB_TT_REFUND:
amount_with_fee = &tl->details.refund->refund_amount;
fee = &tl->details.refund->refund_fee;
fee_dki = &dki->properties.fee_refund;
add_to = &refunds;
// FIXME: where do we check that the refund(s)
// of the coin match the deposit(s) of the coin (by merchant, timestamp, etc.)?
if (GNUNET_OK !=
TALER_amount_add (&refunds,
&refunds,
amount_with_fee))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
TALER_amount_add (&expenditures,
&expenditures,
fee))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
/* Check if this refund is within the remit of the aggregation
we are investigating, if so, include it in the totals. */
if ( (0 == memcmp (merchant_pub,
&tl->details.refund->merchant_pub,
sizeof (struct TALER_MerchantPublicKeyP))) &&
(0 == memcmp (h_proposal_data,
&tl->details.refund->h_proposal_data,
sizeof (struct GNUNET_HashCode))) )
{
if (GNUNET_OK !=
TALER_amount_add (&merchant_loss,
&merchant_loss,
amount_with_fee))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
TALER_amount_add (merchant_fees,
merchant_fees,
fee))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
}
break;
}
GNUNET_assert (NULL != add_to); /* check switch was exhaustive */
if (GNUNET_OK !=
TALER_amount_add (add_to,
add_to,
amount_with_fee))
{
/* overflow in history already!? inconceivable! Bad DB! */
GNUNET_break (0);
report_coin_inconsistency (coin_pub,
add_to,
amount_with_fee,
"could not add coin's contribution to total");
return GNUNET_SYSERR;
}
/* Check that the fees given in the transaction list and in dki match */
TALER_amount_ntoh (&tmp,
fee_dki);
if (0 !=
TALER_amount_cmp (&tmp,
fee))
{
/* Disagreement in fee structure within DB! */
/* Disagreement in fee structure within DB, should be impossible! */
GNUNET_break (0);
report_coin_inconsistency (coin_pub,
&tmp,
fee,
"coin's fee in transaction and in denomination data differ");
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
TALER_amount_add (&fees,
&fees,
fee))
{
/* overflow in fee total? inconceivable! Bad DB! */
GNUNET_break (0);
report_coin_inconsistency (coin_pub,
fee,
&fees,
"could not add coin's fee to total fees");
return GNUNET_SYSERR;
}
} /* for 'tl' */
/* Finally, calculate total balance change, i.e. expenditures minus refunds */
/* Calculate total balance change, i.e. expenditures minus refunds */
if (GNUNET_SYSERR ==
TALER_amount_subtract (final_expenditures,
TALER_amount_subtract (&spent,
&expenditures,
&refunds))
{
/* refunds above expenditures? inconceivable! Bad DB! */
GNUNET_break (0);
/* refunds above expenditures? Bad! */
report_coin_inconsistency (coin_pub,
&expenditures,
&refunds,
"could not subtract refunded amount from expenditures");
return GNUNET_SYSERR;
}
/* Now check that 'spent' is less or equal than total coin value */
TALER_amount_ntoh (&value,
&dki->properties.value);
if (1 == TALER_amount_cmp (&spent,
&value))
{
/* spent > value */
report_coin_inconsistency (coin_pub,
&spent,
&value,
"accepted deposits (minus refunds) exceeds denomination value");
return GNUNET_SYSERR;
}
/* Finally, update @a merchant_gain by subtracting what he "lost" from refunds */
if (GNUNET_SYSERR ==
TALER_amount_subtract (merchant_gain,
merchant_gain,
&merchant_loss))
{
/* refunds above deposits? Bad! */
report_coin_inconsistency (coin_pub,
merchant_gain,
&merchant_loss,
"merchant was granted more refunds than he deposited");
return GNUNET_SYSERR;
}
return GNUNET_OK;
}