diff options
| author | Christian Grothoff <christian@grothoff.org> | 2017-03-19 06:50:08 +0100 | 
|---|---|---|
| committer | Christian Grothoff <christian@grothoff.org> | 2017-03-19 06:50:08 +0100 | 
| commit | 424b327395a403464ab32d056b780ba761a44770 (patch) | |
| tree | 6a3dad033690fdfc5e626e76cdf381b9bf03d77d /src/auditor/taler-auditor.c | |
| parent | 9b4d0634e15fd026a887b4e425abc2a75ddeff04 (diff) | |
implement check_transaction_history()
Diffstat (limited to 'src/auditor/taler-auditor.c')
| -rw-r--r-- | src/auditor/taler-auditor.c | 201 | 
1 files changed, 147 insertions, 54 deletions
diff --git a/src/auditor/taler-auditor.c b/src/auditor/taler-auditor.c index 1dd9a874..c6b0fbf8 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;  }  | 
