gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] branch master updated: -work purse_deposit conflict han


From: gnunet
Subject: [taler-exchange] branch master updated: -work purse_deposit conflict handling
Date: Mon, 16 May 2022 14:01:08 +0200

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

grothoff pushed a commit to branch master
in repository exchange.

The following commit(s) were added to refs/heads/master by this push:
     new d803d86b -work purse_deposit conflict handling
d803d86b is described below

commit d803d86bf9c69947f6b6e37caf8800abbd24135f
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Mon May 16 14:01:04 2022 +0200

    -work purse_deposit conflict handling
---
 src/exchange/taler-exchange-httpd_purses_merge.c |   2 +-
 src/exchange/taler-exchange-httpd_responses.c    |   2 +
 src/exchangedb/plugin_exchangedb_postgres.c      |   2 +-
 src/lib/exchange_api_purse_deposit.c             | 332 ++++++++++++++++++-----
 src/lib/exchange_api_purse_merge.c               |   2 +-
 src/testing/test_exchange_p2p.c                  |  32 +++
 6 files changed, 302 insertions(+), 70 deletions(-)

diff --git a/src/exchange/taler-exchange-httpd_purses_merge.c 
b/src/exchange/taler-exchange-httpd_purses_merge.c
index 9ca973d1..397492d0 100644
--- a/src/exchange/taler-exchange-httpd_purses_merge.c
+++ b/src/exchange/taler-exchange-httpd_purses_merge.c
@@ -275,7 +275,7 @@ merge_transaction (void *cls,
                                          &merge_timestamp,
                                          &partner_url,
                                          &reserve_pub);
-    if (qs < 0)
+    if (qs <= 0)
     {
       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
         return qs;
diff --git a/src/exchange/taler-exchange-httpd_responses.c 
b/src/exchange/taler-exchange-httpd_responses.c
index 725e08d9..e2b33c79 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -566,6 +566,8 @@ TEH_RESPONSE_reply_coin_insufficient_funds (
     connection,
     TALER_ErrorCode_get_http_status_safe (ec),
     TALER_JSON_pack_ec (ec),
+    GNUNET_JSON_pack_data_auto ("coin_pub",
+                                coin_pub),
     GNUNET_JSON_pack_array_steal ("history",
                                   history));
 }
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c 
b/src/exchangedb/plugin_exchangedb_postgres.c
index e6b86813..4d5efb9c 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -3620,7 +3620,7 @@ prepare_statements (struct PostgresClosure *pg)
       ",merge_timestamp"
       ",partner_base_url"
       " FROM purse_merges"
-      " JOIN partners USING (partner_serial_id)"
+      " LEFT JOIN partners USING (partner_serial_id)"
       " WHERE purse_pub=$1;",
       1),
     /* Used in #postgres_do_account_merge() */
diff --git a/src/lib/exchange_api_purse_deposit.c 
b/src/lib/exchange_api_purse_deposit.c
index f71bb389..67f5355d 100644
--- a/src/lib/exchange_api_purse_deposit.c
+++ b/src/lib/exchange_api_purse_deposit.c
@@ -33,6 +33,28 @@
 #include "exchange_api_curl_defaults.h"
 
 
+/**
+ * Information we track per coin.
+ */
+struct Coin
+{
+  /**
+   * Coin's public key.
+   */
+  struct TALER_CoinSpendPublicKeyP coin_pub;
+
+  /**
+   * Coin's denomination.
+   */
+  struct TALER_DenominationHashP h_denom_pub;
+
+  /**
+   * How much did we say the coin contributed.
+   */
+  struct TALER_Amount contribution;
+};
+
+
 /**
  * @brief A purse create with deposit handle
  */
@@ -83,7 +105,7 @@ struct TALER_EXCHANGE_PurseDepositHandle
   /**
    * Array of @e num_deposits coins we are depositing.
    */
-  struct TALER_CoinSpendPublicKeyP *coins;
+  struct Coin *coins;
 
   /**
    * Number of coins we are depositing.
@@ -122,7 +144,6 @@ handle_purse_deposit_finished (void *cls,
     break;
   case MHD_HTTP_OK:
     {
-      const struct TALER_EXCHANGE_Keys *key_state;
       struct GNUNET_TIME_Timestamp etime;
       struct TALER_ExchangeSignatureP exchange_sig;
       struct TALER_ExchangePublicKeyP exchange_pub;
@@ -158,9 +179,8 @@ handle_purse_deposit_finished (void *cls,
         dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
         break;
       }
-      key_state = TALER_EXCHANGE_get_keys (pch->exchange);
       if (GNUNET_OK !=
-          TALER_EXCHANGE_test_signing_key (key_state,
+          TALER_EXCHANGE_test_signing_key (keys,
                                            &exchange_pub))
       {
         GNUNET_break_op (0);
@@ -191,85 +211,260 @@ handle_purse_deposit_finished (void *cls,
     /* This should never happen, either us or the exchange is buggy
        (or API version conflict); just pass JSON reply to the application */
     dr.hr.ec = TALER_JSON_get_error_code (j);
-    dr.hr.hint = TALER_JSON_get_error_hint (j);
     break;
   case MHD_HTTP_FORBIDDEN:
     dr.hr.ec = TALER_JSON_get_error_code (j);
-    dr.hr.hint = TALER_JSON_get_error_hint (j);
     /* Nothing really to verify, exchange says one of the signatures is
        invalid; as we checked them, this should never happen, we
        should pass the JSON reply to the application */
     break;
   case MHD_HTTP_NOT_FOUND:
     dr.hr.ec = TALER_JSON_get_error_code (j);
-    dr.hr.hint = TALER_JSON_get_error_hint (j);
     /* Nothing really to verify, this should never
        happen, we should pass the JSON reply to the application */
     break;
   case MHD_HTTP_CONFLICT:
+    dr.hr.ec = TALER_JSON_get_error_code (j);
+    switch (dr.hr.ec)
     {
-      const char *partner_url = NULL;
-      struct TALER_CoinSpendPublicKeyP coin_pub;
-      struct TALER_CoinSpendSignatureP coin_sig;
-      struct TALER_Amount amount;
-      struct GNUNET_JSON_Specification spec[] = {
-        GNUNET_JSON_spec_fixed_auto ("coin_sig",
-                                     &coin_sig),
-        GNUNET_JSON_spec_fixed_auto ("coin_pub",
-                                     &coin_pub),
-        GNUNET_JSON_spec_mark_optional (
-          GNUNET_JSON_spec_string ("partner_url",
-                                   &partner_url),
-          NULL),
-        TALER_JSON_spec_amount ("amount",
-                                keys->currency,
-                                &amount),
-        GNUNET_JSON_spec_end ()
-      };
-      bool found = false;
-
-      if (GNUNET_OK !=
-          GNUNET_JSON_parse (j,
-                             spec,
-                             NULL, NULL))
+    case TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA:
       {
-        GNUNET_break_op (0);
-        dr.hr.http_status = 0;
-        dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+        const char *partner_url = NULL;
+        struct TALER_CoinSpendPublicKeyP coin_pub;
+        struct TALER_CoinSpendSignatureP coin_sig;
+        struct TALER_Amount amount;
+        struct GNUNET_JSON_Specification spec[] = {
+          GNUNET_JSON_spec_fixed_auto ("coin_sig",
+                                       &coin_sig),
+          GNUNET_JSON_spec_fixed_auto ("coin_pub",
+                                       &coin_pub),
+          GNUNET_JSON_spec_mark_optional (
+            GNUNET_JSON_spec_string ("partner_url",
+                                     &partner_url),
+            NULL),
+          TALER_JSON_spec_amount ("amount",
+                                  keys->currency,
+                                  &amount),
+          GNUNET_JSON_spec_end ()
+        };
+        bool found = false;
+
+        if (GNUNET_OK !=
+            GNUNET_JSON_parse (j,
+                               spec,
+                               NULL, NULL))
+        {
+          GNUNET_break_op (0);
+          dr.hr.http_status = 0;
+          dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+          break;
+        }
+        for (unsigned int i = 0; i<pch->num_deposits; i++)
+          if (0 == GNUNET_memcmp (&coin_pub,
+                                  &pch->coins[i].coin_pub))
+          {
+            found = true;
+            break;
+          }
+        if (! found)
+        {
+          /* proof is about a coin we did not even deposit */
+          GNUNET_break_op (0);
+          dr.hr.http_status = 0;
+          dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+          break;
+        }
+        if (NULL == partner_url)
+          partner_url = pch->base_url;
+        if (GNUNET_OK !=
+            TALER_wallet_purse_deposit_verify (
+              partner_url,
+              &pch->purse_pub,
+              &amount,
+              &coin_pub,
+              &coin_sig))
+        {
+          GNUNET_break_op (0);
+          dr.hr.http_status = 0;
+          dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+          break;
+        }
+        /* meta data conflict is real! */
         break;
       }
-      for (unsigned int i = 0; i<pch->num_deposits; i++)
-        if (0 == GNUNET_memcmp (&coin_pub,
-                                &pch->coins[i]))
+    case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
+      {
+        json_t *history;
+        struct TALER_Amount total;
+        struct TALER_DenominationHashP h_denom_pub;
+        const struct TALER_EXCHANGE_DenomPublicKey *dki;
+        struct TALER_CoinSpendPublicKeyP coin_pub;
+        struct GNUNET_JSON_Specification spec[] = {
+          GNUNET_JSON_spec_fixed_auto ("coin_pub",
+                                       &coin_pub),
+          GNUNET_JSON_spec_json ("history",
+                                 &history),
+          GNUNET_JSON_spec_end ()
+        };
+        bool found = false;
+        const struct Coin *my_coin;
+
+        if (GNUNET_OK !=
+            GNUNET_JSON_parse (j,
+                               spec,
+                               NULL, NULL))
         {
-          found = true;
+          GNUNET_break_op (0);
+          dr.hr.http_status = 0;
+          dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
           break;
         }
-      if (! found)
-      {
-        /* proof is about a coin we did not even deposit */
-        GNUNET_break_op (0);
-        dr.hr.http_status = 0;
-        dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
-        break;
+        for (unsigned int i = 0; i<pch->num_deposits; i++)
+        {
+          if (0 == GNUNET_memcmp (&coin_pub,
+                                  &pch->coins[i].coin_pub))
+          {
+            found = true;
+            my_coin = &pch->coins[i];
+            break;
+          }
+        }
+        if (! found)
+        {
+          /* proof is about a coin we did not even deposit */
+          GNUNET_break_op (0);
+          dr.hr.http_status = 0;
+          dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+          break;
+        }
+        dki = TALER_EXCHANGE_get_denomination_key_by_hash (
+          keys,
+          &my_coin->h_denom_pub);
+        if (NULL == dki)
+        {
+          dr.hr.http_status = 0;
+          dr.hr.ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN;
+          GNUNET_break_op (0);
+          break;
+        }
+        if (GNUNET_OK !=
+            TALER_EXCHANGE_verify_coin_history (dki,
+                                                dki->value.currency,
+                                                &coin_pub,
+                                                history,
+                                                &h_denom_pub,
+                                                &total))
+        {
+          GNUNET_break_op (0);
+          dr.hr.http_status = 0;
+          dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+          json_decref (history);
+          break;
+        }
+        json_decref (history);
+        if (0 >
+            TALER_amount_add (&total,
+                              &total,
+                              &my_coin->contribution))
+        {
+          /* clearly not OK if our transaction would have caused
+             the overflow... */
+          break;
+        }
+        if (0 >= TALER_amount_cmp (&total,
+                                   &dki->value))
+        {
+          /* transaction should have still fit */
+          GNUNET_break (0);
+          dr.hr.http_status = 0;
+          dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+          break;
+        }
+        /* everything OK, proof of double-spending was provided */
       }
-      if (NULL == partner_url)
-        partner_url = pch->base_url;
-      if (GNUNET_OK !=
-          TALER_wallet_purse_deposit_verify (
-            partner_url,
-            &pch->purse_pub,
-            &amount,
-            &coin_pub,
-            &coin_sig))
+    case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
       {
-        GNUNET_break_op (0);
-        dr.hr.http_status = 0;
-        dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
-        break;
+        json_t *history;
+        struct TALER_Amount total;
+        struct TALER_DenominationHashP h_denom_pub;
+        const struct Coin *my_coin;
+        const struct TALER_EXCHANGE_DenomPublicKey *dki;
+        struct TALER_CoinSpendPublicKeyP coin_pub;
+        struct GNUNET_JSON_Specification spec[] = {
+          GNUNET_JSON_spec_fixed_auto ("coin_pub",
+                                       &coin_pub),
+          GNUNET_JSON_spec_json ("history",
+                                 &history),
+          GNUNET_JSON_spec_end ()
+        };
+        bool found = false;
+
+        if (GNUNET_OK !=
+            GNUNET_JSON_parse (j,
+                               spec,
+                               NULL, NULL))
+        {
+          GNUNET_break_op (0);
+          dr.hr.http_status = 0;
+          dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+          break;
+        }
+        for (unsigned int i = 0; i<pch->num_deposits; i++)
+        {
+          if (0 == GNUNET_memcmp (&coin_pub,
+                                  &pch->coins[i].coin_pub))
+          {
+            found = true;
+            my_coin = &pch->coins[i];
+            break;
+          }
+        }
+        if (! found)
+        {
+          /* proof is about a coin we did not even deposit */
+          GNUNET_break_op (0);
+          dr.hr.http_status = 0;
+          dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+          break;
+        }
+        dki = TALER_EXCHANGE_get_denomination_key_by_hash (
+          keys,
+          &my_coin->h_denom_pub);
+        memset (&h_denom_pub,
+                0,
+                sizeof (h_denom_pub));
+        if (GNUNET_OK !=
+            TALER_EXCHANGE_verify_coin_history (dki,
+                                                dki->value.currency,
+                                                &coin_pub,
+                                                history,
+                                                &h_denom_pub,
+                                                &total))
+        {
+          GNUNET_break_op (0);
+          dr.hr.http_status = 0;
+          dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+          json_decref (history);
+          break;
+        }
+        json_decref (history);
+        if (0 == GNUNET_memcmp (&dki->h_key,
+                                &h_denom_pub))
+        {
+          /* sorry, this proves nothing */
+          GNUNET_break_op (0);
+          dr.hr.http_status = 0;
+          dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+          break;
+        }
+        /* everything OK, proof of conflicting denomination was provided */
       }
-      /* conflict is real! */
-    }
+    default:
+      GNUNET_break_op (0);
+      dr.hr.http_status = 0;
+      dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+      break;
+    } /* ec switch */
     break;
   case MHD_HTTP_GONE:
     /* could happen if denomination was revoked */
@@ -277,18 +472,15 @@ handle_purse_deposit_finished (void *cls,
        signature here, alas tricky in case our /keys
        is outdated => left to clients */
     dr.hr.ec = TALER_JSON_get_error_code (j);
-    dr.hr.hint = TALER_JSON_get_error_hint (j);
     break;
   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     dr.hr.ec = TALER_JSON_get_error_code (j);
-    dr.hr.hint = TALER_JSON_get_error_hint (j);
     /* Server had an internal issue; we should retry, but this API
        leaves this to the application */
     break;
   default:
     /* unexpected response code */
     dr.hr.ec = TALER_JSON_get_error_code (j);
-    dr.hr.hint = TALER_JSON_get_error_hint (j);
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "Unexpected response code %u/%d for exchange deposit\n",
                 (unsigned int) response_code,
@@ -296,6 +488,10 @@ handle_purse_deposit_finished (void *cls,
     GNUNET_break_op (0);
     break;
   }
+  if (TALER_EC_NONE == dr.hr.ec)
+    dr.hr.hint = NULL;
+  else
+    dr.hr.hint = TALER_ErrorCode_get_hint (dr.hr.ec);
   pch->cb (pch->cb_cls,
            &dr);
   TALER_EXCHANGE_purse_deposit_cancel (pch);
@@ -361,11 +557,11 @@ TALER_EXCHANGE_purse_deposit (
                                     "/");
   pch->num_deposits = num_deposits;
   pch->coins = GNUNET_new_array (num_deposits,
-                                 struct TALER_CoinSpendPublicKeyP);
+                                 struct Coin);
   for (unsigned int i = 0; i<num_deposits; i++)
   {
     const struct TALER_EXCHANGE_PurseDeposit *deposit = &deposits[i];
-    struct TALER_CoinSpendPublicKeyP *coin_pub = &pch->coins[i];
+    struct Coin *coin = &pch->coins[i];
     json_t *jdeposit;
     struct TALER_CoinSpendSignatureP coin_sig;
 #if FIXME_OEC
@@ -390,7 +586,9 @@ TALER_EXCHANGE_purse_deposit (
     }
 #endif
     GNUNET_CRYPTO_eddsa_key_get_public (&deposit->coin_priv.eddsa_priv,
-                                        &coin_pub->eddsa_pub);
+                                        &coin->coin_pub.eddsa_pub);
+    coin->h_denom_pub = deposit->h_denom_pub;
+    coin->contribution = deposit->amount;
     TALER_wallet_purse_deposit_sign (
       pch->base_url,
       &pch->purse_pub,
@@ -413,7 +611,7 @@ TALER_EXCHANGE_purse_deposit (
       TALER_JSON_pack_denom_sig ("ub_sig",
                                  &deposit->denom_sig),
       GNUNET_JSON_pack_data_auto ("coin_pub",
-                                  coin_pub),
+                                  &coin->coin_pub),
       GNUNET_JSON_pack_data_auto ("coin_sig",
                                   &coin_sig));
     GNUNET_assert (0 ==
diff --git a/src/lib/exchange_api_purse_merge.c 
b/src/lib/exchange_api_purse_merge.c
index 2becd946..a32b44d4 100644
--- a/src/lib/exchange_api_purse_merge.c
+++ b/src/lib/exchange_api_purse_merge.c
@@ -118,7 +118,7 @@ make_payto (const char *exchange_url,
 
   end = GNUNET_STRINGS_data_to_string (
     reserve_pub,
-    sizeof (reserve_pub),
+    sizeof (*reserve_pub),
     pub_str,
     sizeof (pub_str));
   *end = '\0';
diff --git a/src/testing/test_exchange_p2p.c b/src/testing/test_exchange_p2p.c
index e4d6f5b2..b3e98daa 100644
--- a/src/testing/test_exchange_p2p.c
+++ b/src/testing/test_exchange_p2p.c
@@ -112,6 +112,8 @@ run (void *cls,
      */
     CMD_TRANSFER_TO_EXCHANGE ("create-reserve-1",
                               "EUR:5.01"),
+    CMD_TRANSFER_TO_EXCHANGE ("create-reserve-2",
+                              "EUR:5.01"),
     TALER_TESTING_cmd_reserve_poll ("poll-reserve-1",
                                     "create-reserve-1",
                                     "EUR:5.01",
@@ -122,6 +124,11 @@ run (void *cls,
                                                  bc.user42_payto,
                                                  bc.exchange_payto,
                                                  "create-reserve-1"),
+    TALER_TESTING_cmd_check_bank_admin_transfer ("check-create-reserve-2",
+                                                 "EUR:5.01",
+                                                 bc.user42_payto,
+                                                 bc.exchange_payto,
+                                                 "create-reserve-2"),
     /**
      * Make a reserve exist, according to the previous
      * transfer.
@@ -193,6 +200,13 @@ run (void *cls,
       "EUR:1",
       MHD_HTTP_OK),
 #endif
+    /* Test conflicting merge */
+    TALER_TESTING_cmd_purse_merge (
+      "purse-merge-into-reserve",
+      MHD_HTTP_CONFLICT,
+      "push-get-contract",
+      "create-reserve-2"),
+
     TALER_TESTING_cmd_end ()
   };
   struct TALER_TESTING_Command pull[] = {
@@ -241,6 +255,24 @@ run (void *cls,
       "create-reserve-1",
       "EUR:2",
       MHD_HTTP_OK),
+#endif
+    /* create 2nd purse for a deposit conflict */
+    TALER_TESTING_cmd_purse_create_with_reserve (
+      "purse-create-with-reserve-2",
+      MHD_HTTP_OK,
+      "{\"amount\":\"EUR:4\",\"summary\":\"beer\"}",
+      true /* upload contract */,
+      GNUNET_TIME_UNIT_MINUTES, /* expiration */
+      "create-reserve-1"),
+#if FIXME_RESERVE_HISTORY
+    TALER_TESTING_cmd_purse_deposit_coins (
+      "purse-deposit-coins-conflict",
+      MHD_HTTP_CONFLICT,
+      0 /* min age */,
+      "purse-create-with-reserve-2",
+      "withdraw-coin-1",
+      "EUR:4.01",
+      NULL),
 #endif
     TALER_TESTING_cmd_end ()
   };

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

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