aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2017-03-19 06:50:08 +0100
committerChristian Grothoff <christian@grothoff.org>2017-03-19 06:50:08 +0100
commit424b327395a403464ab32d056b780ba761a44770 (patch)
tree6a3dad033690fdfc5e626e76cdf381b9bf03d77d /src
parent9b4d0634e15fd026a887b4e425abc2a75ddeff04 (diff)
implement check_transaction_history()
Diffstat (limited to 'src')
-rw-r--r--src/auditor/taler-auditor.c201
1 files changed, 147 insertions, 54 deletions
diff --git a/src/auditor/taler-auditor.c b/src/auditor/taler-auditor.c
index 1dd9a8748..c6b0fbf8d 100644
--- a/src/auditor/taler-auditor.c
+++ b/src/auditor/taler-auditor.c
@@ -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! */
- 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! */
+ /* Disagreement in fee structure within DB, should be impossible! */
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;
}