gnunet-svn
[Top][All Lists]
Advanced

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

[taler-merchant] branch master updated: use new /batch-deposit API in me


From: gnunet
Subject: [taler-merchant] branch master updated: use new /batch-deposit API in merchant
Date: Sat, 02 Jul 2022 23:00:09 +0200

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

grothoff pushed a commit to branch master
in repository merchant.

The following commit(s) were added to refs/heads/master by this push:
     new 09a8501e use new /batch-deposit API in merchant
09a8501e is described below

commit 09a8501e94ac33051299631022ae9e519ee58a99
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Sat Jul 2 23:00:07 2022 +0200

    use new /batch-deposit API in merchant
---
 .../taler-merchant-httpd_post-orders-ID-pay.c      | 549 +++++++++++++--------
 src/lib/merchant_api_post_order_pay.c              |   9 +-
 2 files changed, 336 insertions(+), 222 deletions(-)

diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c 
b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
index 82419668..a3699114 100644
--- a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
+++ b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
@@ -164,6 +164,11 @@ struct DepositConfirmation
 struct ExchangeGroup
 {
 
+  /**
+   * Payment context this group is part of.
+   */
+  struct PayContext *pc;
+
   /**
    * Handle to the batch deposit operation we are performing for this
    * exchange, NULL after the operation is done.
@@ -178,10 +183,15 @@ struct ExchangeGroup
   struct TMH_EXCHANGES_FindOperation *fo;
 
   /**
-   * URL of the exchange that issued this coin.
+   * URL of the exchange that issued this coin. Aliases
+   * the exchange URL of one of the coins, do not free!
    */
-  char *exchange_url;
+  const char *exchange_url;
 
+  /**
+   * true if we already tried a forced /keys download.
+   */
+  bool tried_force_keys;
 };
 
 
@@ -205,7 +215,7 @@ struct PayContext
    * Array with @e num_exchange exchanges we are depositing
    * coins into.
    */
-  struct ExchangeGroup *eg;
+  struct ExchangeGroup **egs;
 
   /**
    * Array with @e coins_cnt coins we are despositing.
@@ -240,18 +250,6 @@ struct PayContext
    */
   struct MHD_Response *response;
 
-  /**
-   * Handle for operation to lookup /keys (and auditors) from
-   * the exchange used for this transaction; NULL if no operation is
-   * pending.
-   */
-  struct TMH_EXCHANGES_FindOperation *fo;
-
-  /**
-   * URL of the exchange used for the last @e fo.
-   */
-  const char *current_exchange;
-
   /**
    * Placeholder for #TALER_MHD_parse_post_json() to keep its internal state.
    */
@@ -396,19 +394,14 @@ struct PayContext
   unsigned int retry_counter;
 
   /**
-   * Number of transactions still pending.  Initially set to
-   * @e coins_cnt, decremented on each transaction that
-   * successfully finished.
+   * Number of batch transactions pending.
    */
-  unsigned int pending;
+  unsigned int pending_at_eg;
 
   /**
-   * Number of transactions still pending for the currently selected
-   * exchange.  Initially set to the number of coins started at the
-   * exchange, decremented on each transaction that successfully
-   * finished.  Once it hits zero, we pick the next exchange.
+   * Number of coin deposits pending.
    */
-  unsigned int pending_at_ce;
+  unsigned int pending;
 
   /**
    * HTTP status code to use for the reply, i.e 200 for "OK".
@@ -425,11 +418,6 @@ struct PayContext
    */
   enum GNUNET_GenericReturnValue suspended;
 
-  /**
-   * true if we already tried a forced /keys download.
-   */
-  bool tried_force_keys;
-
 };
 
 
@@ -699,11 +687,15 @@ pay_context_cleanup (void *cls)
     GNUNET_free (dc->exchange_url);
   }
   GNUNET_free (pc->dc);
-  if (NULL != pc->fo)
+  for (unsigned int i = 0; i<pc->num_exchanges; i++)
   {
-    TMH_EXCHANGES_find_exchange_cancel (pc->fo);
-    pc->fo = NULL;
+    struct ExchangeGroup *eg = pc->egs[i];
+
+    if (NULL != eg->fo)
+      TMH_EXCHANGES_find_exchange_cancel (eg->fo);
+    GNUNET_free (eg);
   }
+  GNUNET_free (pc->egs);
   if (NULL != pc->response)
   {
     MHD_destroy_response (pc->response);
@@ -718,16 +710,6 @@ pay_context_cleanup (void *cls)
 }
 
 
-/**
- * Find the exchange we need to talk to for the next
- * pending deposit permission.
- *
- * @param pc payment context we are processing
- */
-static void
-find_next_exchange (struct PayContext *pc);
-
-
 /**
  * Execute the DB transaction.  If required (from
  * soft/serialization errors), the transaction can be
@@ -878,7 +860,7 @@ kyc_cb (
  */
 static void
 check_kyc (struct PayContext *pc,
-           const struct DepositConfirmation *dc)
+           const struct ExchangeGroup *eg)
 {
   enum GNUNET_DB_QueryStatus qs;
   struct KycContext *kc;
@@ -887,7 +869,7 @@ check_kyc (struct PayContext *pc,
   qs = TMH_db->account_kyc_get_status (TMH_db->cls,
                                        pc->hc->instance->settings.id,
                                        &pc->wm->h_wire,
-                                       dc->exchange_url,
+                                       eg->exchange_url,
                                        &kyc_cb,
                                        kc);
   if (qs < 0)
@@ -911,7 +893,7 @@ check_kyc (struct PayContext *pc,
     {
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                   "Not re-checking KYC status at `%s', as we already recently 
asked\n",
-                  dc->exchange_url);
+                  eg->exchange_url);
       GNUNET_free (kc);
       return;
     }
@@ -919,13 +901,23 @@ check_kyc (struct PayContext *pc,
   kc->mi = pc->hc->instance;
   kc->mi->rc++;
   kc->wm = pc->wm;
-  kc->exchange_url = GNUNET_strdup (dc->exchange_url);
+  kc->exchange_url = GNUNET_strdup (eg->exchange_url);
   kc->h_contract_terms = pc->h_contract_terms;
-  kc->coin_pub = dc->cdd.coin_pub;
+  /* find one of the coins of the batch */
+  for (unsigned int i = 0; i<pc->coins_cnt; i++)
+  {
+    struct DepositConfirmation *dc = &pc->dc[i];
+
+    if (0 != strcmp (eg->exchange_url,
+                     pc->dc[i].exchange_url))
+      continue;
+    kc->coin_pub = dc->cdd.coin_pub;
+    break;
+  }
   GNUNET_CONTAINER_DLL_insert (kc_head,
                                kc_tail,
                                kc);
-  kc->fo = TMH_EXCHANGES_find_exchange (dc->exchange_url,
+  kc->fo = TMH_EXCHANGES_find_exchange (kc->exchange_url,
                                         NULL,
                                         GNUNET_NO,
                                         &process_kyc_with_exchange,
@@ -939,87 +931,156 @@ check_kyc (struct PayContext *pc,
 
 
 /**
- * Callback to handle a deposit permission's response.
+ * Handle case where the batch deposit completed
+ * with a status of #MHD_HTTP_OK.
  *
- * @param cls a `struct DepositConfirmation` (i.e. a pointer
- *   into the global array of confirmations and an index for this call
- *   in that array). That way, the last executed callback can detect
- *   that no other confirmations are on the way, and can pack a response
- *   for the wallet
- * @param dr HTTP response code details
+ * @param eg group that completed
+ * @param dr response from the server
  */
 static void
-deposit_cb (void *cls,
-            const struct TALER_EXCHANGE_DepositResult *dr)
+handle_batch_deposit_ok (struct ExchangeGroup *eg,
+                         const struct TALER_EXCHANGE_BatchDepositResult *dr)
 {
-  struct DepositConfirmation *dc = cls;
-  struct PayContext *pc = dc->pc;
+  struct PayContext *pc = eg->pc;
+  unsigned int j = 0;
+  enum GNUNET_DB_QueryStatus qs
+    = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
 
-  dc->dh = NULL;
-  GNUNET_assert (GNUNET_YES == pc->suspended);
-  pc->pending_at_ce--;
-  switch (dr->hr.http_status)
+  /* store result to DB */
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Storing successful payment %s (%s) at instance `%s'\n",
+              pc->hc->infix,
+              GNUNET_h2s (&pc->h_contract_terms.hash),
+              pc->hc->instance->settings.id);
+  for (unsigned int r = 0; r<MAX_RETRIES; r++)
   {
-  case MHD_HTTP_OK:
+    TMH_db->preflight (TMH_db->cls);
+    if (GNUNET_OK !=
+        TMH_db->start (TMH_db->cls,
+                       "batch-deposit-insert-confirmation"))
+    {
+      resume_pay_with_response (
+        pc,
+        MHD_HTTP_INTERNAL_SERVER_ERROR,
+        TALER_MHD_MAKE_JSON_PACK (
+          TALER_JSON_pack_ec (
+            TALER_EC_GENERIC_DB_START_FAILED),
+          TMH_pack_exchange_reply (&dr->hr)));
+      return;
+    }
+    for (unsigned int i = 0; i<pc->coins_cnt; i++)
     {
-      /* store result to DB */
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Storing successful payment %s (%s) at instance `%s'\n",
-                  pc->hc->infix,
-                  GNUNET_h2s (&pc->h_contract_terms.hash),
-                  pc->hc->instance->settings.id);
-      TMH_db->preflight (TMH_db->cls);
+      struct DepositConfirmation *dc = &pc->dc[i];
+
+      if (0 != strcmp (eg->exchange_url,
+                       pc->dc[i].exchange_url))
+        continue;
+      if (dc->found_in_db)
+        continue;
+      /* NOTE: We might want to check if the order was fully paid concurrently
+         by some other wallet here, and if so, issue an auto-refund. Right now,
+         it is possible to over-pay if two wallets literally make a concurrent
+         payment, as the earlier check for 'paid' is not in the same 
transaction
+         scope as this 'insert' operation. */
+      qs = TMH_db->insert_deposit (
+        TMH_db->cls,
+        pc->hc->instance->settings.id,
+        dr->details.success.deposit_timestamp,
+        &pc->h_contract_terms,
+        &dc->cdd.coin_pub,
+        dc->exchange_url,
+        &dc->cdd.amount,
+        &dc->deposit_fee,
+        &dc->refund_fee,
+        &dc->wire_fee,
+        &pc->wm->h_wire,
+        &dr->details.success.exchange_sigs[j++],
+        dr->details.success.exchange_pub);
+      if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
       {
-        enum GNUNET_DB_QueryStatus qs;
-
-        /* NOTE: We might want to check if the order was fully paid 
concurrently
-           by some other wallet here, and if so, issue an auto-refund. Right 
now,
-           it is possible to over-pay if two wallets literally make a 
concurrent
-           payment, as the earlier check for 'paid' is not in the same 
transaction
-           scope as this 'insert' operation. */
-        qs = TMH_db->insert_deposit (
-          TMH_db->cls,
-          pc->hc->instance->settings.id,
-          dr->details.success.deposit_timestamp,
-          &pc->h_contract_terms,
-          &dc->cdd.coin_pub,
-          dc->exchange_url,
-          &dc->cdd.amount,
-          &dc->deposit_fee,
-          &dc->refund_fee,
-          &dc->wire_fee,
-          &pc->wm->h_wire,
-          dr->details.success.exchange_sig,
-          dr->details.success.exchange_pub);
-        if (0 > qs)
-        {
-          /* Special report if retries insufficient */
-          if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
-          {
-            execute_pay_transaction (pc);
-            return;
-          }
-          /* Always report on hard error as well to enable diagnostics */
-          GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
-          /* Forward error including 'proof' for the body */
-          resume_pay_with_error (pc,
-                                 MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                 TALER_EC_GENERIC_DB_STORE_FAILED,
-                                 "insert_deposit");
-          return;
-        }
+        TMH_db->rollback (TMH_db->cls);
+        continue;
+      }
+      if (0 > qs)
+      {
+        /* Always report on hard error as well to enable diagnostics */
+        GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
+        /* Forward error including 'proof' for the body */
+        resume_pay_with_error (pc,
+                               MHD_HTTP_INTERNAL_SERVER_ERROR,
+                               TALER_EC_GENERIC_DB_STORE_FAILED,
+                               "insert_deposit");
+        return;
       }
+    }
+    qs = TMH_db->commit (TMH_db->cls);
+    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+    {
+      TMH_db->rollback (TMH_db->cls);
+      continue;
+    }
+    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+    {
+      GNUNET_break (0);
+      resume_pay_with_error (pc,
+                             MHD_HTTP_INTERNAL_SERVER_ERROR,
+                             TALER_EC_GENERIC_DB_COMMIT_FAILED,
+                             "insert_deposit");
+    }
+    break; /* DB transaction succeeded */
+  } /* FOR DB retries */
+  if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+  {
+    resume_pay_with_error (pc,
+                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                           TALER_EC_GENERIC_DB_SOFT_FAILURE,
+                           "insert_deposit");
+    return;
+  }
 
-      dc->found_in_db = true; /* well, at least NOW it'd be true ;-) */
-      pc->pending--;
+  /* Transaction is done, mark affected coins as complete as well. */
+  for (unsigned int i = 0; i<pc->coins_cnt; i++)
+  {
+    struct DepositConfirmation *dc = &pc->dc[i];
 
-      if (0 != pc->pending_at_ce)
-        return; /* still more to do with current exchange */
-      check_kyc (pc,
-                 dc);
-      find_next_exchange (pc);
-      return;
-    }
+    if (0 != strcmp (eg->exchange_url,
+                     pc->dc[i].exchange_url))
+      continue;
+    if (dc->found_in_db)
+      continue;
+    dc->found_in_db = true;     /* well, at least NOW it'd be true ;-) */
+    pc->pending--;
+  }
+  check_kyc (pc,
+             eg);
+}
+
+
+/**
+ * Callback to handle a batch deposit permission's response.
+ *
+ * @param cls a `struct ExchangeGroup`
+ * @param dr HTTP response code details
+ */
+static void
+batch_deposit_cb (
+  void *cls,
+  const struct TALER_EXCHANGE_BatchDepositResult *dr)
+{
+  struct ExchangeGroup *eg = cls;
+  struct PayContext *pc = eg->pc;
+
+  eg->bdh = NULL;
+  GNUNET_assert (GNUNET_YES == pc->suspended);
+  pc->pending_at_eg--;
+  switch (dr->hr.http_status)
+  {
+  case MHD_HTTP_OK:
+    handle_batch_deposit_ok (eg,
+                             dr);
+    if (0 == pc->pending_at_eg)
+      execute_pay_transaction (eg->pc);
+    return;
   default:
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "Deposit operation failed with HTTP code %u/%d\n",
@@ -1050,7 +1111,7 @@ deposit_cb (void *cls,
       return;
     }
 
-    /* Forward error, adding the "coin_pub" for which the
+    /* Forward error, adding the "exchange_url" for which the
        error was being generated */
     if (TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS == dr->hr.ec)
     {
@@ -1061,8 +1122,8 @@ deposit_cb (void *cls,
           TALER_JSON_pack_ec (
             TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_FUNDS),
           TMH_pack_exchange_reply (&dr->hr),
-          GNUNET_JSON_pack_data_auto ("coin_pub",
-                                      &dc->cdd.coin_pub)));
+          GNUNET_JSON_pack_data_auto ("exchange_url",
+                                      &eg->exchange_url)));
       return;
     }
     resume_pay_with_response (
@@ -1072,8 +1133,8 @@ deposit_cb (void *cls,
         TALER_JSON_pack_ec (
           TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNEXPECTED_STATUS),
         TMH_pack_exchange_reply (&dr->hr),
-        GNUNET_JSON_pack_data_auto ("coin_pub",
-                                    &dc->cdd.coin_pub)));
+        GNUNET_JSON_pack_data_auto ("exchange_url",
+                                    &eg->exchange_url)));
     return;
   } /* end switch */
 }
@@ -1082,7 +1143,7 @@ deposit_cb (void *cls,
 /**
  * Function called with the result of our exchange lookup.
  *
- * @param cls the `struct PayContext`
+ * @param cls the `struct ExchangeGroup`
  * @param hr HTTP response details
  * @param exchange_handle NULL if exchange was not found to be acceptable
  * @param payto_uri payto://-URI of the exchange
@@ -1100,15 +1161,18 @@ process_pay_with_exchange (
   const struct TALER_Amount *wire_fee,
   bool exchange_trusted)
 {
-  struct PayContext *pc = cls;
+  struct ExchangeGroup *eg = cls;
+  struct PayContext *pc = eg->pc;
   struct TMH_HandlerContext *hc = pc->hc;
   const struct TALER_EXCHANGE_Keys *keys;
+  unsigned int group_size;
 
   (void) payto_uri;
-  pc->fo = NULL;
+  eg->fo = NULL;
   GNUNET_assert (GNUNET_YES == pc->suspended);
   if (NULL == hr)
   {
+    pc->pending_at_eg--;
     resume_pay_with_response (
       pc,
       MHD_HTTP_GATEWAY_TIMEOUT,
@@ -1119,6 +1183,7 @@ process_pay_with_exchange (
   if ( (MHD_HTTP_OK != hr->http_status) ||
        (NULL == exchange_handle) )
   {
+    pc->pending_at_eg--;
     resume_pay_with_response (
       pc,
       MHD_HTTP_BAD_GATEWAY,
@@ -1131,6 +1196,7 @@ process_pay_with_exchange (
   keys = TALER_EXCHANGE_get_keys (exchange_handle);
   if (NULL == keys)
   {
+    pc->pending_at_eg--;
     GNUNET_break (0); /* should not be possible if HTTP status is #MHD_HTTP_OK 
*/
     resume_pay_with_error (pc,
                            MHD_HTTP_BAD_GATEWAY,
@@ -1139,44 +1205,43 @@ process_pay_with_exchange (
     return;
   }
 
-  /* Initiate /deposit operation for all coins of
+  /* Initiate /batch-deposit operation for all coins of
      the current exchange (!) */
-  GNUNET_assert (0 == pc->pending_at_ce);
+  group_size = 0;
   for (unsigned int i = 0; i<pc->coins_cnt; i++)
   {
     struct DepositConfirmation *dc = &pc->dc[i];
     const struct TALER_EXCHANGE_DenomPublicKey *denom_details;
-    enum TALER_ErrorCode ec;
     unsigned int http_status;
+    enum TALER_ErrorCode ec;
     bool is_age_restricted_denom = false;
 
-    if (NULL != dc->dh)
-      continue; /* we were here before (can happen due to
-                         tried_force_keys logic), don't go again */
-    if (dc->found_in_db)
+    if (0 != strcmp (eg->exchange_url,
+                     pc->dc[i].exchange_url))
       continue;
-    if (0 != strcmp (dc->exchange_url,
-                     pc->current_exchange))
+    if (dc->found_in_db)
       continue;
+
     denom_details
       = TALER_EXCHANGE_get_denomination_key_by_hash (keys,
                                                      &dc->cdd.h_denom_pub);
     if (NULL == denom_details)
     {
-      if (! pc->tried_force_keys)
+      if (! eg->tried_force_keys)
       {
         /* let's try *forcing* a re-download of /keys from the exchange.
            Maybe the wallet has seen /keys that we missed. */
-        pc->tried_force_keys = true;
-        pc->fo = TMH_EXCHANGES_find_exchange (pc->current_exchange,
+        eg->tried_force_keys = true;
+        eg->fo = TMH_EXCHANGES_find_exchange (eg->exchange_url,
                                               pc->wm->wire_method,
                                               GNUNET_YES,
                                               &process_pay_with_exchange,
-                                              pc);
-        if (NULL != pc->fo)
+                                              eg);
+        if (NULL != eg->fo)
           return;
       }
       /* Forcing failed or we already did it, give up */
+      pc->pending_at_eg--;
       resume_pay_with_response (
         pc,
         MHD_HTTP_BAD_REQUEST,
@@ -1191,6 +1256,8 @@ process_pay_with_exchange (
               (json_t *) TALER_EXCHANGE_get_keys_raw (exchange_handle)))));
       return;
     }
+    dc->deposit_fee = denom_details->fees.deposit;
+    dc->refund_fee = denom_details->fees.refund;
 
     if (GNUNET_OK !=
         TMH_AUDITORS_check_dk (exchange_handle,
@@ -1199,19 +1266,20 @@ process_pay_with_exchange (
                                &http_status,
                                &ec))
     {
-      if (! pc->tried_force_keys)
+      if (! eg->tried_force_keys)
       {
         /* let's try *forcing* a re-download of /keys from the exchange.
            Maybe the wallet has seen auditors that we missed. */
-        pc->tried_force_keys = true;
-        pc->fo = TMH_EXCHANGES_find_exchange (pc->current_exchange,
+        eg->tried_force_keys = true;
+        eg->fo = TMH_EXCHANGES_find_exchange (eg->exchange_url,
                                               pc->wm->wire_method,
                                               GNUNET_YES,
                                               &process_pay_with_exchange,
-                                              pc);
-        if (NULL != pc->fo)
+                                              eg);
+        if (NULL != eg->fo)
           return;
       }
+      pc->pending_at_eg--;
       resume_pay_with_response (
         pc,
         http_status,
@@ -1226,10 +1294,10 @@ process_pay_with_exchange (
     /* Now that we have the details about the denomination, we can verify age
      * restriction requirements, if applicable. Note that denominations with an
      * age_mask equal to zero always pass the age verification.  */
-    is_age_restricted_denom = 0 < denom_details->key.age_mask.bits;
+    is_age_restricted_denom = (0 != denom_details->key.age_mask.bits);
 
-    if (is_age_restricted_denom
-        && (0 < pc->minimum_age))
+    if (is_age_restricted_denom &&
+        (0 < pc->minimum_age))
     {
       /* Minimum age given and restricted coind provided: We need to verify the
        * minimum age */
@@ -1241,7 +1309,6 @@ process_pay_with_exchange (
         code = TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AGE_COMMITMENT_MISSING;
         goto AGE_FAIL;
       }
-
       dc->age_commitment.mask = denom_details->key.age_mask;
       if ((dc->age_commitment.num + 1) !=
           __builtin_popcount (dc->age_commitment.mask.bits))
@@ -1251,17 +1318,16 @@ process_pay_with_exchange (
           TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AGE_COMMITMENT_SIZE_MISMATCH;
         goto AGE_FAIL;
       }
-
       if (GNUNET_OK !=
           TALER_age_commitment_verify (
             &dc->age_commitment,
             pc->minimum_age,
             &dc->minimum_age_sig))
         code = TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AGE_VERIFICATION_FAILED;
-
 AGE_FAIL:
       if (0 < code)
       {
+        pc->pending_at_eg--;
         GNUNET_free (dc->age_commitment.keys);
         resume_pay_with_response (
           pc,
@@ -1279,97 +1345,126 @@ AGE_FAIL:
                                  &dc->cdd.h_age_commitment);
       GNUNET_free (dc->age_commitment.keys);
     }
-    else if (is_age_restricted_denom)
+    else if (is_age_restricted_denom && dc->no_age_commitment)
     {
       /* The contract did not ask for a minimum_age but the client paid
-       * with a coin that has age restriction enabled.  We need the hash
+       * with a coin that has age restriction enabled.  We lack the hash
        * of the age commitment in this case in order to verify the coin
        * and to deposit it with the exchange. */
-      if (dc->no_h_age_commitment)
-      {
-        GNUNET_break_op (0);
-        resume_pay_with_response (
-          pc,
-          MHD_HTTP_BAD_REQUEST,
-          TALER_MHD_MAKE_JSON_PACK (
-            TALER_JSON_pack_ec (
-              
TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AGE_COMMITMENT_HASH_MISSING),
-            GNUNET_JSON_pack_data_auto ("h_denom_pub",
-                                        &denom_details->h_key)));
-        return;
-      }
+      pc->pending_at_eg--;
+      GNUNET_break_op (0);
+      resume_pay_with_response (
+        pc,
+        MHD_HTTP_BAD_REQUEST,
+        TALER_MHD_MAKE_JSON_PACK (
+          TALER_JSON_pack_ec (
+            TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AGE_COMMITMENT_HASH_MISSING),
+          GNUNET_JSON_pack_data_auto ("h_denom_pub",
+                                      &denom_details->h_key)));
+      return;
     }
+    group_size++;
+  }
 
-    dc->deposit_fee = denom_details->fees.deposit;
-    dc->refund_fee = denom_details->fees.refund;
-    dc->wire_fee = *wire_fee;
-    GNUNET_assert (NULL != pc->wm);
-    TMH_db->preflight (TMH_db->cls);
+  if (0 == group_size)
+  {
+    GNUNET_break (0);
+    pc->pending_at_eg--;
+    if (0 == pc->pending_at_eg)
+      execute_pay_transaction (pc);
+    return;
+  }
+
+  {
+    struct TALER_EXCHANGE_CoinDepositDetail cdds[group_size];
+    struct TALER_EXCHANGE_DepositContractDetail dcd = {
+      .wire_deadline = pc->wire_transfer_deadline,
+      .merchant_payto_uri = pc->wm->payto_uri,
+      .wire_salt = pc->wm->wire_salt,
+      .h_contract_terms = pc->h_contract_terms,
+      .extension_details = NULL /* FIXME-OEC */,
+      .timestamp = pc->timestamp,
+      .merchant_pub = hc->instance->merchant_pub,
+      .refund_deadline = pc->refund_deadline
+    };
+    enum TALER_ErrorCode ec;
+
+    for (unsigned int i = 0; i<pc->coins_cnt; i++)
     {
-      struct TALER_EXCHANGE_DepositContractDetail dcd = {
-        .wire_deadline = pc->wire_transfer_deadline,
-        .merchant_payto_uri = pc->wm->payto_uri,
-        .wire_salt = pc->wm->wire_salt,
-        .h_contract_terms = pc->h_contract_terms,
-        .extension_details = NULL /* FIXME-OEC */,
-        .timestamp = pc->timestamp,
-        .merchant_pub = hc->instance->merchant_pub,
-        .refund_deadline = pc->refund_deadline
-      };
+      struct DepositConfirmation *dc = &pc->dc[i];
 
-      dc->dh = TALER_EXCHANGE_deposit (exchange_handle,
-                                       &dcd,
-                                       &dc->cdd,
-                                       &deposit_cb,
-                                       dc,
-                                       &ec);
+      if (dc->found_in_db)
+        continue;
+      if (0 != strcmp (dc->exchange_url,
+                       eg->exchange_url))
+        continue;
+      cdds[i] = dc->cdd;
+      dc->wire_fee = *wire_fee;
+      GNUNET_assert (NULL != pc->wm);
     }
-    if (NULL == dc->dh)
+    eg->bdh = TALER_EXCHANGE_batch_deposit (exchange_handle,
+                                            &dcd,
+                                            group_size,
+                                            cdds,
+                                            &batch_deposit_cb,
+                                            eg,
+                                            &ec);
+    if (NULL == eg->bdh)
     {
       /* Signature was invalid or some other constraint was not satisfied.  If
          the exchange was unavailable, we'd get that information in the
          callback. */
+      pc->pending_at_eg--;
       GNUNET_break_op (0);
       resume_pay_with_response (
         pc,
         TALER_ErrorCode_get_http_status_safe (ec),
         TALER_MHD_MAKE_JSON_PACK (
           TALER_JSON_pack_ec (ec),
-          GNUNET_JSON_pack_uint64 ("coin_idx",
-                                   i)));
+          GNUNET_JSON_pack_string ("exchange_url",
+                                   eg->exchange_url)));
       return;
     }
     if (TMH_force_audit)
-      TALER_EXCHANGE_deposit_force_dc (dc->dh);
-    pc->pending_at_ce++;
+      TALER_EXCHANGE_batch_deposit_force_dc (eg->bdh);
   }
 }
 
 
 /**
- * Find the exchange we need to talk to for the next
- * pending deposit permission.
+ * Start batch deposits for all exchanges involved
+ * in this payment.
  *
  * @param pc payment context we are processing
  */
 static void
-find_next_exchange (struct PayContext *pc)
+start_batch_deposits (struct PayContext *pc)
 {
-  GNUNET_assert (0 == pc->pending_at_ce);
-  for (unsigned int i = 0; i<pc->coins_cnt; i++)
+  for (unsigned int i = 0; i<pc->num_exchanges; i++)
   {
-    struct DepositConfirmation *dc = &pc->dc[i];
+    struct ExchangeGroup *eg = pc->egs[i];
+    bool have_coins = false;
 
-    if (dc->found_in_db)
-      continue;
+    for (unsigned int j = 0; j<pc->coins_cnt; j++)
+    {
+      struct DepositConfirmation *dc = &pc->dc[j];
 
-    pc->current_exchange = dc->exchange_url;
-    pc->fo = TMH_EXCHANGES_find_exchange (pc->current_exchange,
+      if (0 != strcmp (eg->exchange_url,
+                       pc->dc[j].exchange_url))
+        continue;
+      if (dc->found_in_db)
+        continue;
+      have_coins = true;
+      break;
+    }
+    if (! have_coins)
+      continue; /* no coins left to deposit at this exchange */
+    eg->fo = TMH_EXCHANGES_find_exchange (eg->exchange_url,
                                           pc->wm->wire_method,
                                           GNUNET_NO,
                                           &process_pay_with_exchange,
-                                          pc);
-    if (NULL == pc->fo)
+                                          eg);
+    if (NULL == eg->fo)
     {
       GNUNET_break (0);
       resume_pay_with_error (pc,
@@ -1378,13 +1473,10 @@ find_next_exchange (struct PayContext *pc)
                              "Failed to lookup exchange by URL");
       return;
     }
-    return;
+    pc->pending_at_eg++;
   }
-  pc->current_exchange = NULL;
-  /* We are done with all the HTTP requests, go back and try
-     the 'big' database transaction! (It should work now!) */
-  GNUNET_assert (0 == pc->pending);
-  execute_pay_transaction (pc);
+  if (0 == pc->pending_at_eg)
+    execute_pay_transaction (pc);
 }
 
 
@@ -1672,7 +1764,7 @@ check_payment_sufficient (struct PayContext *pc)
      * Sum of fees of *all* the different exchanges of all the coins are
      * higher than the fixed limit that the merchant is willing to pay.  The
      * difference must be paid by the customer.
-     *///
+     */
     struct TALER_Amount excess_fee;
 
     /* compute fee amount to be covered by customer */
@@ -1945,7 +2037,7 @@ execute_pay_transaction (struct PayContext *pc)
     /* Ok, we need to first go to the network to process more coins.
        We that interaction in *tiny* transactions (hence the rollback
        above). */
-    find_next_exchange (pc);
+    start_batch_deposits (pc);
     return;
   }
 
@@ -2140,6 +2232,7 @@ parse_pay (struct PayContext *pc)
         GNUNET_JSON_spec_end ()
       };
       enum GNUNET_GenericReturnValue res;
+      bool have_eg = false;
 
       res = TALER_MHD_parse_json_data (pc->connection,
                                        coin,
@@ -2187,8 +2280,8 @@ parse_pay (struct PayContext *pc)
               : GNUNET_SYSERR;
       }
 
-      // Check the consistency of the (potential) age restriction
-      // information.
+      /* Check the consistency of the (potential) age restriction
+       * information. */
       if (dc->no_age_commitment != dc->no_minimum_age_sig)
       {
         GNUNET_break_op (0);
@@ -2204,6 +2297,29 @@ parse_pay (struct PayContext *pc)
               ? GNUNET_NO
               : GNUNET_SYSERR;
       }
+
+      /* Setup exchange group */
+      for (unsigned int i = 0; i<pc->num_exchanges; i++)
+      {
+        if (0 ==
+            strcmp (pc->egs[i]->exchange_url,
+                    GNUNET_strdup (exchange_url)))
+        {
+          have_eg = true;
+          break;
+        }
+      }
+      if (! have_eg)
+      {
+        struct ExchangeGroup *eg;
+
+        eg = GNUNET_new (struct ExchangeGroup);
+        eg->pc = pc;
+        eg->exchange_url = dc->exchange_url;
+        GNUNET_array_append (pc->egs,
+                             pc->num_exchanges,
+                             eg);
+      }
     }
   }
   GNUNET_JSON_parse_free (spec);
@@ -2563,11 +2679,6 @@ handle_pay_timeout (void *cls)
   GNUNET_assert (GNUNET_YES == pc->suspended);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Resuming pay with error after timeout\n");
-  if (NULL != pc->fo)
-  {
-    TMH_EXCHANGES_find_exchange_cancel (pc->fo);
-    pc->fo = NULL;
-  }
   resume_pay_with_error (pc,
                          MHD_HTTP_GATEWAY_TIMEOUT,
                          TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT,
diff --git a/src/lib/merchant_api_post_order_pay.c 
b/src/lib/merchant_api_post_order_pay.c
index b5e2c706..4e935965 100644
--- a/src/lib/merchant_api_post_order_pay.c
+++ b/src/lib/merchant_api_post_order_pay.c
@@ -284,17 +284,20 @@ parse_conflict (struct TALER_MERCHANT_OrderPayHandle *oph,
                 const json_t *json)
 {
   json_t *ereply;
-  struct TALER_CoinSpendPublicKeyP coin_pub;
+  const char *exchange_url;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_json ("exchange_reply",
                            &ereply),
-    GNUNET_JSON_spec_fixed_auto ("coin_pub",
-                                 &coin_pub),
+    GNUNET_JSON_spec_string ("exchange_url",
+                             &exchange_url),
     GNUNET_JSON_spec_end ()
   };
+  struct TALER_CoinSpendPublicKeyP coin_pub;
   struct GNUNET_JSON_Specification hspec[] = {
     GNUNET_JSON_spec_json ("history",
                            &oph->error_history),
+    GNUNET_JSON_spec_fixed_auto ("coin_pub",
+                                 &coin_pub),
     GNUNET_JSON_spec_end ()
   };
   enum TALER_ErrorCode ec = TALER_JSON_get_error_code (json);

-- 
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]