gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-exchange] 02/08: check coin history consistency


From: gnunet
Subject: [taler-exchange] 02/08: check coin history consistency
Date: Fri, 27 Mar 2020 11:26:39 +0100

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository exchange.

commit 62b8ca0bd3bfa47a864bf35d1c1d4b346e98d720
Author: Christian Grothoff <address@hidden>
AuthorDate: Fri Mar 27 09:35:42 2020 +0100

    check coin history consistency
---
 src/auditor/taler-helper-auditor-coins.c | 272 ++++++++++++++++++++++++++++++-
 1 file changed, 264 insertions(+), 8 deletions(-)

diff --git a/src/auditor/taler-helper-auditor-coins.c 
b/src/auditor/taler-helper-auditor-coins.c
index 9f9f6dc9..5bcf636b 100644
--- a/src/auditor/taler-helper-auditor-coins.c
+++ b/src/auditor/taler-helper-auditor-coins.c
@@ -38,7 +38,7 @@
  * Set to a VERY low value here for testing. Practical values may be
  * in the millions.
  */
-#define MAX_COIN_SUMMARIES 4
+#define MAX_COIN_HISTORIES 4
 
 /**
  * Use a 1 day grace period to deal with clocks not being perfectly 
synchronized.
@@ -169,6 +169,84 @@ static json_t *report_refreshs_hanging;
  */
 static struct TALER_Amount total_refresh_hanging;
 
+/**
+ * Coin and associated transaction history.
+ */
+struct CoinHistory
+{
+  /**
+   * Public key of the coin.
+   */
+  struct TALER_CoinSpendPublicKeyP coin_pub;
+
+  /**
+   * The transaction list for the @a coin_pub.
+   */
+  struct TALER_EXCHANGEDB_TransactionList *tl;
+};
+
+/**
+ * Array of transaction histories for coins.  The index is based on the coin's
+ * public key.  Entries are replaced whenever we have a collision.
+ */
+static struct CoinHistory coin_histories[MAX_COIN_HISTORIES];
+
+
+/**
+ * Return the index we should use for @a coin_pub in #coin_histories.
+ *
+ * @param coin_pub a coin's public key
+ * @return index for caching this coin's history in #coin_histories
+ */
+static unsigned int
+coin_history_index (const struct TALER_CoinSpendPublicKeyP *coin_pub)
+{
+  uint32_t i;
+
+  memcpy (&i,
+          coin_pub,
+          sizeof (i));
+  return i % MAX_COIN_HISTORIES;
+}
+
+
+/**
+ * Add a coin history to our in-memory cache.
+ *
+ * @param coin_pub public key of the coin to cache
+ * @param tl history to store
+ */
+static void
+cache_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
+               struct TALER_EXCHANGEDB_TransactionList *tl)
+{
+  unsigned int i = coin_history_index (coin_pub);
+
+  if (NULL != coin_histories[i].tl)
+    TALER_ARL_edb->free_coin_transaction_list (TALER_ARL_edb->cls,
+                                               coin_histories[i].tl);
+  coin_histories[i].coin_pub = *coin_pub;
+  coin_histories[i].tl = tl;
+}
+
+
+/**
+ * Obtain a coin's history from our in-memory cache.
+ *
+ * @param coin_pub public key of the coin to cache
+ * @return NULL if @a coin_pub is not in the cache
+ */
+static struct TALER_EXCHANGEDB_TransactionList *
+get_cached_history (const struct TALER_CoinSpendPublicKeyP *coin_pub)
+{
+  unsigned int i = coin_history_index (coin_pub);
+
+  if (GNUNET_memcmp (coin_pub,
+                     &coin_histories[i].coin_pub))
+    return coin_histories[i].tl;
+  return NULL;
+}
+
 
 /* ***************************** Report logic **************************** */
 
@@ -362,6 +440,136 @@ report_row_inconsistency (const char *table,
 }
 
 
+/* ************* Analyze history of a coin ******************** */
+
+
+/**
+ * Obtain @a coin_pub's history, verify it, report inconsistencies
+ * and store the result in our cache.
+ *
+ * @param coin_pub public key of the coin to check the history of
+ * @param rowid a row identifying the transaction
+ * @param operation operation matching @a rowid
+ * @param value value of the respective coin's denomination
+ * @return database status code, negative on failures
+ */
+static enum GNUNET_DB_QueryStatus
+check_coin_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                    uint64_t rowid,
+                    const char *operation,
+                    const struct TALER_Amount *value)
+{
+  struct TALER_EXCHANGEDB_TransactionList *tl;
+  enum GNUNET_DB_QueryStatus qs;
+  struct TALER_Amount total;
+  struct TALER_Amount spent;
+  struct TALER_Amount refunded;
+  struct TALER_Amount deposit_fee;
+  int have_refund;
+
+  qs = TALER_ARL_edb->get_coin_transactions (TALER_ARL_edb->cls,
+                                             TALER_ARL_esession,
+                                             coin_pub,
+                                             GNUNET_YES,
+                                             &tl);
+  if (0 >= qs)
+    return qs;
+
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_get_zero (value->currency,
+                                        &refunded));
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_get_zero (value->currency,
+                                        &spent));
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_get_zero (value->currency,
+                                        &deposit_fee));
+  for (struct TALER_EXCHANGEDB_TransactionList *pos = tl;
+       NULL != pos;
+       pos = pos->next)
+  {
+    switch (pos->type)
+    {
+    case TALER_EXCHANGEDB_TT_DEPOSIT:
+      /* spent += pos->amount_with_fee */
+      GNUNET_assert (GNUNET_OK ==
+                     TALER_amount_add (&spent,
+                                       &spent,
+                                       
&pos->details.deposit->amount_with_fee));
+      deposit_fee = pos->details.deposit->deposit_fee;
+      break;
+    case TALER_EXCHANGEDB_TT_MELT:
+      /* spent += pos->amount_with_fee */
+      GNUNET_assert (GNUNET_OK ==
+                     TALER_amount_add (&spent,
+                                       &spent,
+                                       &pos->details.melt->amount_with_fee));
+      break;
+    case TALER_EXCHANGEDB_TT_REFUND:
+      /* refunded += pos->refund_amount - pos->refund_fee */
+      GNUNET_assert (GNUNET_OK ==
+                     TALER_amount_add (&refunded,
+                                       &refunded,
+                                       &pos->details.refund->refund_amount));
+      GNUNET_assert (GNUNET_OK ==
+                     TALER_amount_add (&spent,
+                                       &spent,
+                                       &pos->details.refund->refund_fee));
+      have_refund = GNUNET_YES;
+      break;
+    case TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP:
+      /* refunded += pos->value */
+      GNUNET_assert (GNUNET_OK ==
+                     TALER_amount_add (&refunded,
+                                       &refunded,
+                                       &pos->details.old_coin_recoup->value));
+    case TALER_EXCHANGEDB_TT_RECOUP:
+      /* spent += pos->value */
+      GNUNET_assert (GNUNET_OK ==
+                     TALER_amount_add (&spent,
+                                       &spent,
+                                       &pos->details.recoup->value));
+      break;
+    case TALER_EXCHANGEDB_TT_RECOUP_REFRESH:
+      /* spent += pos->value */
+      GNUNET_assert (GNUNET_OK ==
+                     TALER_amount_add (&spent,
+                                       &spent,
+                                       &pos->details.recoup_refresh->value));
+      break;
+    }
+  }
+
+  if (have_refund)
+  {
+    /* If we gave any refund, also discount ONE deposit fee */
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_amount_add (&refunded,
+                                     &refunded,
+                                     &deposit_fee));
+  }
+  /* total coin value = original value plus refunds */
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_add (&total,
+                                   &refunded,
+                                   value));
+  if (1 ==
+      TALER_amount_cmp (&spent,
+                        &total))
+  {
+    /* spent > total: bad */
+    report_amount_arithmetic_inconsistency (operation,
+                                            rowid,
+                                            &spent,
+                                            &total,
+                                            -1);
+  }
+  cache_history (coin_pub,
+                 tl);
+  return qs;
+}
+
+
 /* ************************* Analyze coins ******************** */
 /* This logic checks that the exchange did the right thing for each
    coin, checking deposits, refunds, refresh* and known_coins
@@ -776,6 +984,7 @@ withdraw_cb (void *cls,
   (void) reserve_sig;
   (void) execution_date;
   (void) amount_with_fee;
+
   GNUNET_assert (rowid >= ppc.last_withdraw_serial_id); /* should be 
monotonically increasing */
   ppc.last_withdraw_serial_id = rowid + 1;
 
@@ -930,9 +1139,12 @@ reveal_data_cb (void *cls,
 
 /**
  * Check that the @a coin_pub is a known coin with a proper
- * signature for denominatinon @a denom_pub. If not, TALER_ARL_report
+ * signature for denominatinon @a denom_pub. If not, report
  * a loss of @a loss_potential.
  *
+ * @param operation which operation is this about
+ * @param issue denomination key information about the coin
+ * @param rowid which row is this operation in
  * @param coin_pub public key of a coin
  * @param denom_pub expected denomination of the coin
  * @param loss_potential how big could the loss be if the coin is
@@ -941,13 +1153,35 @@ reveal_data_cb (void *cls,
  *  #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
  */
 static enum GNUNET_DB_QueryStatus
-check_known_coin (const struct TALER_CoinSpendPublicKeyP *coin_pub,
+check_known_coin (const char *operation,
+                  const struct TALER_DenominationKeyValidityPS *issue,
+                  uint64_t rowid,
+                  const struct TALER_CoinSpendPublicKeyP *coin_pub,
                   const struct TALER_DenominationPublicKey *denom_pub,
                   const struct TALER_Amount *loss_potential)
 {
   struct TALER_CoinPublicInfo ci;
   enum GNUNET_DB_QueryStatus qs;
 
+  if (NULL == get_cached_history (coin_pub))
+  {
+    struct TALER_Amount value;
+
+    TALER_amount_ntoh (&value,
+                       &issue->value);
+    qs = check_coin_history (coin_pub,
+                             rowid,
+                             operation,
+                             &value);
+    if (0 > qs)
+    {
+      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+      return qs;
+    }
+    GNUNET_break (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs);
+  }
+
+
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Checking denomination signature on %s\n",
               TALER_B2S (coin_pub));
@@ -966,11 +1200,11 @@ check_known_coin (const struct TALER_CoinSpendPublicKeyP 
*coin_pub,
   {
     TALER_ARL_report (report_bad_sig_losses,
                       json_pack ("{s:s, s:I, s:o, s:o}",
-                                 "operation", "known-coin",
-                                 "row", (json_int_t) -1,
+                                 "operation", operation,
+                                 "row", (json_int_t) rowid,
                                  "loss", TALER_JSON_from_amount (
                                    loss_potential),
-                                 "key_pub", GNUNET_JSON_from_data_auto (
+                                 "coin_pub", GNUNET_JSON_from_data_auto (
                                    coin_pub)));
     GNUNET_assert (GNUNET_OK ==
                    TALER_amount_add (&total_bad_sig_loss,
@@ -1037,7 +1271,10 @@ refresh_session_cb (void *cls,
     cc->qs = qs;
     return GNUNET_SYSERR;
   }
-  qs = check_known_coin (coin_pub,
+  qs = check_known_coin ("melt",
+                         issue,
+                         rowid,
+                         coin_pub,
                          denom_pub,
                          amount_with_fee);
   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
@@ -1377,7 +1614,10 @@ deposit_cb (void *cls,
     cc->qs = qs;
     return GNUNET_SYSERR;
   }
-  qs = check_known_coin (coin_pub,
+  qs = check_known_coin ("deposit",
+                         issue,
+                         rowid,
+                         coin_pub,
                          denom_pub,
                          amount_with_fee);
   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
@@ -1738,6 +1978,18 @@ check_recoup (struct CoinContext *cc,
     cc->qs = qs;
     return GNUNET_SYSERR;
   }
+  qs = check_known_coin ("recoup",
+                         issue,
+                         rowid,
+                         &coin->coin_pub,
+                         denom_pub,
+                         amount);
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+  {
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    cc->qs = qs;
+    return GNUNET_SYSERR;
+  }
   {
     struct TALER_RecoupRequestPS pr = {
       .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP),
@@ -1789,6 +2041,10 @@ check_recoup (struct CoinContext *cc,
                                    "loss", TALER_JSON_from_amount (amount),
                                    "coin_pub", GNUNET_JSON_from_data_auto (
                                      &coin->coin_pub)));
+      GNUNET_assert (GNUNET_OK ==
+                     TALER_amount_add (&total_bad_sig_loss,
+                                       &total_bad_sig_loss,
+                                       amount));
     }
     GNUNET_assert (GNUNET_OK ==
                    TALER_amount_add (&ds->denom_recoup,

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]