implement check_transaction_history()
This commit is contained in:
parent
9b4d0634e1
commit
424b327395
@ -23,9 +23,6 @@
|
|||||||
* the wire transfers from the bank. This needs to be checked separately!
|
* the wire transfers from the bank. This needs to be checked separately!
|
||||||
* - Similarly, we do not check that the outgoing wire transfers match those
|
* - Similarly, we do not check that the outgoing wire transfers match those
|
||||||
* given in the 'wire_out' table. This needs to be checked separately!
|
* 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 "platform.h"
|
||||||
#include <gnunet/gnunet_util_lib.h>
|
#include <gnunet/gnunet_util_lib.h>
|
||||||
@ -2002,16 +1999,17 @@ struct WireCheckContext
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Check coin's transaction history for plausibility. Does NOT check
|
* Check coin's transaction history for plausibility. Does NOT check
|
||||||
* the signatures (those are checked independently), but does check
|
* the signatures (those are checked independently), but does calculate
|
||||||
* that the amounts add up to the picture claimed by the aggregation table.
|
* 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 coin_pub public key of the coin (for reporting)
|
||||||
* @param h_proposal_data hash of the proposal for which we calculate the amount
|
* @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 merchant_pub public key of the merchant (who is allowed to issue refunds)
|
||||||
* @param dki denomination information about the coin
|
* @param dki denomination information about the coin
|
||||||
* @param tl_head head of transaction history to verify
|
* @param tl_head head of transaction history to verify
|
||||||
* @param[out] final amount the coin contributes to the transaction
|
* @param[out] merchant_gain amount the coin contributes to the wire transfer to the merchant
|
||||||
* @param[out] final fees the exchange charged for the transaction
|
* @param[out] merchant_fees fees the exchange charged the merchant for the transaction(s)
|
||||||
* @return #GNUNET_OK on success, #GNUNET_SYSERR on error
|
* @return #GNUNET_OK on success, #GNUNET_SYSERR on error
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
@ -2020,12 +2018,14 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
|||||||
const struct TALER_MerchantPublicKeyP *merchant_pub,
|
const struct TALER_MerchantPublicKeyP *merchant_pub,
|
||||||
const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki,
|
const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki,
|
||||||
const struct TALER_EXCHANGEDB_TransactionList *tl_head,
|
const struct TALER_EXCHANGEDB_TransactionList *tl_head,
|
||||||
struct TALER_Amount *final_expenditures,
|
struct TALER_Amount *merchant_gain,
|
||||||
struct TALER_Amount *final_fees)
|
struct TALER_Amount *merchant_fees)
|
||||||
{
|
{
|
||||||
struct TALER_Amount expenditures;
|
struct TALER_Amount expenditures;
|
||||||
struct TALER_Amount refunds;
|
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);
|
GNUNET_assert (NULL != tl_head);
|
||||||
TALER_amount_get_zero (currency,
|
TALER_amount_get_zero (currency,
|
||||||
@ -2033,16 +2033,22 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
|||||||
TALER_amount_get_zero (currency,
|
TALER_amount_get_zero (currency,
|
||||||
&refunds);
|
&refunds);
|
||||||
TALER_amount_get_zero (currency,
|
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)
|
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 *amount_with_fee;
|
||||||
const struct TALER_Amount *fee;
|
const struct TALER_Amount *fee;
|
||||||
const struct TALER_AmountNBO *fee_dki;
|
const struct TALER_AmountNBO *fee_dki;
|
||||||
struct TALER_Amount *add_to;
|
|
||||||
struct TALER_Amount tmp;
|
struct TALER_Amount tmp;
|
||||||
|
|
||||||
add_to = NULL;
|
|
||||||
// FIXME:
|
// FIXME:
|
||||||
// - for refunds/deposits that apply to this merchant and this contract
|
// - for refunds/deposits that apply to this merchant and this contract
|
||||||
// we need to update the total expenditures/refunds/fees
|
// 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;
|
amount_with_fee = &tl->details.deposit->amount_with_fee;
|
||||||
fee = &tl->details.deposit->deposit_fee;
|
fee = &tl->details.deposit->deposit_fee;
|
||||||
fee_dki = &dki->properties.fee_deposit;
|
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;
|
break;
|
||||||
case TALER_EXCHANGEDB_TT_REFRESH_MELT:
|
case TALER_EXCHANGEDB_TT_REFRESH_MELT:
|
||||||
amount_with_fee = &tl->details.melt->amount_with_fee;
|
amount_with_fee = &tl->details.melt->amount_with_fee;
|
||||||
fee = &tl->details.melt->melt_fee;
|
fee = &tl->details.melt->melt_fee;
|
||||||
fee_dki = &dki->properties.fee_refresh;
|
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;
|
break;
|
||||||
case TALER_EXCHANGEDB_TT_REFUND:
|
case TALER_EXCHANGEDB_TT_REFUND:
|
||||||
amount_with_fee = &tl->details.refund->refund_amount;
|
amount_with_fee = &tl->details.refund->refund_amount;
|
||||||
fee = &tl->details.refund->refund_fee;
|
fee = &tl->details.refund->refund_fee;
|
||||||
fee_dki = &dki->properties.fee_refund;
|
fee_dki = &dki->properties.fee_refund;
|
||||||
add_to = &refunds;
|
if (GNUNET_OK !=
|
||||||
// FIXME: where do we check that the refund(s)
|
TALER_amount_add (&refunds,
|
||||||
// of the coin match the deposit(s) of the coin (by merchant, timestamp, etc.)?
|
&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;
|
break;
|
||||||
}
|
}
|
||||||
GNUNET_assert (NULL != add_to); /* check switch was exhaustive */
|
|
||||||
if (GNUNET_OK !=
|
/* Check that the fees given in the transaction list and in dki match */
|
||||||
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;
|
|
||||||
}
|
|
||||||
TALER_amount_ntoh (&tmp,
|
TALER_amount_ntoh (&tmp,
|
||||||
fee_dki);
|
fee_dki);
|
||||||
if (0 !=
|
if (0 !=
|
||||||
TALER_amount_cmp (&tmp,
|
TALER_amount_cmp (&tmp,
|
||||||
fee))
|
fee))
|
||||||
{
|
{
|
||||||
/* Disagreement in fee structure within DB! */
|
/* Disagreement in fee structure within DB, should be impossible! */
|
||||||
GNUNET_break (0);
|
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;
|
return GNUNET_SYSERR;
|
||||||
}
|
}
|
||||||
} /* for 'tl' */
|
} /* for 'tl' */
|
||||||
|
|
||||||
/* Finally, calculate total balance change, i.e. expenditures minus refunds */
|
/* Calculate total balance change, i.e. expenditures minus refunds */
|
||||||
if (GNUNET_SYSERR ==
|
if (GNUNET_SYSERR ==
|
||||||
TALER_amount_subtract (final_expenditures,
|
TALER_amount_subtract (&spent,
|
||||||
&expenditures,
|
&expenditures,
|
||||||
&refunds))
|
&refunds))
|
||||||
{
|
{
|
||||||
/* refunds above expenditures? inconceivable! Bad DB! */
|
/* refunds above expenditures? Bad! */
|
||||||
GNUNET_break (0);
|
|
||||||
report_coin_inconsistency (coin_pub,
|
report_coin_inconsistency (coin_pub,
|
||||||
&expenditures,
|
&expenditures,
|
||||||
&refunds,
|
&refunds,
|
||||||
"could not subtract refunded amount from expenditures");
|
"could not subtract refunded amount from expenditures");
|
||||||
return GNUNET_SYSERR;
|
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;
|
return GNUNET_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user