gnunet-svn
[Top][All Lists]
Advanced

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

[taler-merchant] branch master updated: fix pay handling for v1 contract


From: gnunet
Subject: [taler-merchant] branch master updated: fix pay handling for v1 contracts, including in test
Date: Tue, 24 Dec 2024 17:51:46 +0100

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 1d7f15a5 fix pay handling for v1 contracts, including in test
1d7f15a5 is described below

commit 1d7f15a524fdeca2304d1fa5bf13dcf0decd8ecc
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Tue Dec 24 17:51:41 2024 +0100

    fix pay handling for v1 contracts, including in test
---
 src/backend/taler-merchant-httpd_contract.c        |   8 +-
 .../taler-merchant-httpd_post-orders-ID-pay.c      | 666 ++++++++++++---------
 .../taler-merchant-httpd_private-post-orders.c     |   5 +-
 src/testing/testing_api_cmd_pay_order.c            | 371 +++++++-----
 4 files changed, 630 insertions(+), 420 deletions(-)

diff --git a/src/backend/taler-merchant-httpd_contract.c 
b/src/backend/taler-merchant-httpd_contract.c
index d311990a..a3547a56 100644
--- a/src/backend/taler-merchant-httpd_contract.c
+++ b/src/backend/taler-merchant-httpd_contract.c
@@ -111,9 +111,16 @@ parse_choices (void *cls,
 
   for (unsigned int i = 0; i<*choices_len; i++)
   {
+    struct TALER_MerchantContractChoice *choice = &(*choices)[i];
     const json_t *jinputs;
     const json_t *joutputs;
     struct GNUNET_JSON_Specification spec[] = {
+      TALER_JSON_spec_amount_any ("amount",
+                                  &choice->amount),
+      GNUNET_JSON_spec_mark_optional (
+        TALER_JSON_spec_amount_any ("max_fee",
+                                    &choice->max_fee),
+        NULL),
       GNUNET_JSON_spec_array_const ("inputs",
                                     &jinputs),
       GNUNET_JSON_spec_array_const ("outputs",
@@ -122,7 +129,6 @@ parse_choices (void *cls,
     };
     const char *error_name;
     unsigned int error_line;
-    struct TALER_MerchantContractChoice *choice = &(*choices)[i];
 
     if (GNUNET_OK !=
         GNUNET_JSON_parse (json_array_get (root, i),
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 4a5c7d3f..ba47d7a6 100644
--- a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
+++ b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
@@ -107,7 +107,7 @@ enum PayPhase
   PP_VALIDATE_TOKENS,
 
   /**
-   * Contract has been paid.
+   * Check if contract has been paid.
    */
   PP_CONTRACT_PAID,
 
@@ -365,6 +365,8 @@ struct ExchangeGroup
  */
 struct PayContext
 {
+  // FIXME: group more entries by phase that initializes them,
+  // like we do for 'validate_tokens'.
 
   /**
    * Stored in a DLL.
@@ -403,16 +405,6 @@ struct PayContext
    */
   struct TokenEnvelope *token_envelopes;
 
-  /**
-   * Array with @e choices_len choices from the contract terms.
-   */
-  struct TALER_MerchantContractChoice *choices;
-
-  /**
-   * Array with @e token_families_len token families from the contract terms.
-   */
-  struct TALER_MerchantContractTokenFamily *token_families;
-
   /**
    * MHD connection to return to
    */
@@ -498,6 +490,7 @@ struct PayContext
    */
   struct TALER_PrivateContractHashP h_contract_terms;
 
+
   /**
    * "h_wire" from @e contract_terms.  Used to identify
    * the instance's wire transfer method.
@@ -505,19 +498,83 @@ struct PayContext
   struct TALER_MerchantWireHashP h_wire;
 
   /**
-   * Maximum fee the merchant is willing to pay, from @e root.
-   * Note that IF the total fee of the exchange is higher, that is
-   * acceptable to the merchant if the customer is willing to
-   * pay the difference
-   * (i.e. amount - max_fee <= actual_amount - actual_fee).
+   * Version of the contract terms.
+   */
+  enum TALER_MerchantContractVersion version;
+
+  /**
+   * @e version dependent details from our contract.
    */
-  struct TALER_Amount max_fee;
+  union
+  {
+
+    struct
+    {
+      /**
+       * Maximum fee the merchant is willing to pay, from @e root.
+       * Note that IF the total fee of the exchange is higher, that is
+       * acceptable to the merchant if the customer is willing to
+       * pay the difference
+       * (i.e. amount - max_fee <= actual_amount - actual_fee).
+       */
+      struct TALER_Amount max_fee;
+
+      /**
+       * Amount from @e root.  This is the amount the merchant expects
+       * to make, minus @e max_fee.
+       */
+      struct TALER_Amount amount;
+    } v0;
+
+    struct
+    {
+
+      /**
+       * Array with @e choices_len choices from the contract terms.
+       */
+      struct TALER_MerchantContractChoice *choices;
+
+      /**
+       * Array with @e token_families_len token families from the contract 
terms.
+       */
+      struct TALER_MerchantContractTokenFamily *token_families;
+
+      /**
+       * Length of the @e choices array.
+       */
+      unsigned int choices_len;
+
+      /**
+       * Length of the @e token_families array.
+       */
+      unsigned int token_families_len;
+
+    } v1;
+
+  } details;
 
   /**
-   * Amount from @e root.  This is the amount the merchant expects
-   * to make, minus @e max_fee.
+   * Results from the phase_validate_tokens()
    */
-  struct TALER_Amount amount;
+  struct
+  {
+
+    /**
+     * Maximum fee the merchant is willing to pay, from @e root.
+     * Note that IF the total fee of the exchange is higher, that is
+     * acceptable to the merchant if the customer is willing to
+     * pay the difference
+     * (i.e. amount - max_fee <= actual_amount - actual_fee).
+     */
+    struct TALER_Amount max_fee;
+
+    /**
+     * Amount from @e root.  This is the amount the merchant expects
+     * to make, minus @e max_fee.
+     */
+    struct TALER_Amount brutto;
+
+  } validate_tokens;
 
   /**
    * Considering all the coins with the "found_in_db" flag
@@ -599,16 +656,6 @@ struct PayContext
    */
   unsigned int output_tokens_len;
 
-  /**
-   * Length of the @e choices array.
-   */
-  unsigned int choices_len;
-
-  /**
-   * Length of the @e token_families array.
-   */
-  unsigned int token_families_len;
-
   /**
    * Number of exchanges involved in the payment. Length
    * of the @e eg array.
@@ -879,7 +926,7 @@ batch_deposit_transaction (const struct ExchangeGroup *eg,
   uint32_t off = 0;
 
   GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_set_zero (pc->amount.currency,
+                 TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
                                         &total_without_fees));
   for (size_t i = 0; i<pc->coins_cnt; i++)
   {
@@ -1691,7 +1738,7 @@ phase_success_response (struct PayContext *pc)
     ? NULL
     : TALER_build_pos_confirmation (pc->pos_key,
                                     pc->pos_alg,
-                                    &pc->amount,
+                                    &pc->validate_tokens.brutto,
                                     pc->timestamp);
   token_sigs = (0 >= pc->output_tokens_len)
     ? NULL
@@ -1903,9 +1950,9 @@ check_payment_sufficient (struct PayContext *pc)
   struct TALER_Amount total_needed;
 
   if (0 == pc->coins_cnt)
-    return TALER_amount_is_zero (&pc->amount);
+    return TALER_amount_is_zero (&pc->validate_tokens.brutto);
   GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_set_zero (pc->amount.currency,
+                 TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
                                         &total_wire_fee));
   for (unsigned int i = 0; i < pc->num_exchanges; i++)
   {
@@ -1942,10 +1989,10 @@ check_payment_sufficient (struct PayContext *pc)
    * amount with fee / and wire fee, for all the coins.
    */
   GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_set_zero (pc->amount.currency,
+                 TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
                                         &acc_fee));
   GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_set_zero (pc->amount.currency,
+                 TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
                                         &acc_amount));
   for (size_t i = 0; i<pc->coins_cnt; i++)
   {
@@ -2013,7 +2060,7 @@ check_payment_sufficient (struct PayContext *pc)
               TALER_amount2s (&total_wire_fee));
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Deposit fee limit for merchant: %s\n",
-              TALER_amount2s (&pc->max_fee));
+              TALER_amount2s (&pc->validate_tokens.max_fee));
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Total refunded amount: %s\n",
               TALER_amount2s (&pc->total_refunded));
@@ -2049,7 +2096,7 @@ check_payment_sufficient (struct PayContext *pc)
                "Overflow adding up amounts"));
     return false;
   }
-  if (-1 == TALER_amount_cmp (&pc->max_fee,
+  if (-1 == TALER_amount_cmp (&pc->validate_tokens.max_fee,
                               &acc_fee))
   {
     /**
@@ -2063,12 +2110,12 @@ check_payment_sufficient (struct PayContext *pc)
     GNUNET_assert (TALER_AAR_RESULT_POSITIVE ==
                    TALER_amount_subtract (&excess_fee,
                                           &acc_fee,
-                                          &pc->max_fee));
+                                          &pc->validate_tokens.max_fee));
     /* add that to the total */
     if (0 >
         TALER_amount_add (&total_needed,
                           &excess_fee,
-                          &pc->amount))
+                          &pc->validate_tokens.brutto))
     {
       GNUNET_break (0);
       pay_end (pc,
@@ -2084,7 +2131,7 @@ check_payment_sufficient (struct PayContext *pc)
   {
     /* Fees are fully covered by the merchant, all we require
        is that the total payment is not below the contract's amount */
-    total_needed = pc->amount;
+    total_needed = pc->validate_tokens.brutto;
   }
 
   /* Do not count refunds towards the payment */
@@ -2123,7 +2170,7 @@ check_payment_sufficient (struct PayContext *pc)
       return false;
     }
     if (-1 < TALER_amount_cmp (&acc_amount,
-                               &pc->amount))
+                               &pc->validate_tokens.brutto))
     {
       GNUNET_break_op (0);
       pay_end (pc,
@@ -2181,13 +2228,13 @@ phase_execute_pay_transaction (struct PayContext *pc)
      (used in check_coin_paid(), check_coin_refunded()
      and check_payment_sufficient()). */
   GNUNET_break (GNUNET_OK ==
-                TALER_amount_set_zero (pc->amount.currency,
+                TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
                                        &pc->total_paid));
   GNUNET_break (GNUNET_OK ==
-                TALER_amount_set_zero (pc->amount.currency,
+                TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
                                        &pc->total_fees_paid));
   GNUNET_break (GNUNET_OK ==
-                TALER_amount_set_zero (pc->amount.currency,
+                TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
                                        &pc->total_refunded));
   for (size_t i = 0; i<pc->coins_cnt; i++)
     pc->dc[i].found_in_db = false;
@@ -2240,7 +2287,7 @@ phase_execute_pay_transaction (struct PayContext *pc)
     else if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     {
       /* UNIQUE constraint violation, meaning this token was already used. */
-      TMH_db->rollback(TMH_db->cls);
+      TMH_db->rollback (TMH_db->cls);
       pay_end (pc,
                TALER_MHD_reply_with_error (pc->connection,
                                            MHD_HTTP_CONFLICT,
@@ -2275,13 +2322,14 @@ phase_execute_pay_transaction (struct PayContext *pc)
     }
     if (pc->deposit_currency_mismatch)
     {
-      TMH_db->rollback(TMH_db->cls);
+      TMH_db->rollback (TMH_db->cls);
       GNUNET_break_op (0);
       pay_end (pc,
                TALER_MHD_reply_with_error (pc->connection,
                                            MHD_HTTP_BAD_REQUEST,
                                            
TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
-                                           pc->amount.currency));
+                                           
pc->validate_tokens.brutto.currency))
+      ;
       return;
     }
   }
@@ -2647,20 +2695,23 @@ sign_token_envelopes (struct PayContext *pc,
  * @param slug slug to search for
  * @return NULL if @a slug was not found
  */
-static struct TALER_MerchantContractTokenFamily *
+static const struct TALER_MerchantContractTokenFamily *
 find_family (const struct PayContext *pc,
              const char *slug)
 {
-  for (unsigned int i = 0; i<pc->token_families_len; i++)
+  for (unsigned int i = 0; i<pc->details.v1.token_families_len; i++)
   {
-    if (0 == strcmp (pc->token_families[i].slug,
+    const struct TALER_MerchantContractTokenFamily *tfi
+      = &pc->details.v1.token_families[i];
+
+    if (0 == strcmp (tfi->slug,
                      slug))
     {
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                   "Token family %s found with %u keys\n",
                   slug,
-                  pc->token_families[i].keys_len);
-      return &pc->token_families[i];
+                  tfi->keys_len);
+      return tfi;
     }
   }
   return NULL;
@@ -2678,178 +2729,173 @@ find_family (const struct PayContext *pc,
 static void
 phase_validate_tokens (struct PayContext *pc)
 {
-  if (NULL == pc->choices || 0 >= pc->choices_len)
+  switch (pc->version)
   {
+  case TALER_MCV_V0:
     /* No tokens to validate */
     pc->phase = PP_PAY_TRANSACTION;
-    return;
-  }
-
-  if (pc->choice_index < 0)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Order `%s' has non-empty choices array but"
-                "request is missing 'choice_index' field\n",
-                pc->order_id);
-    GNUNET_break (0);
-    pay_end (pc,
-             TALER_MHD_reply_with_error (
-               pc->connection,
-               MHD_HTTP_BAD_REQUEST,
-               TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_CHOICE_INDEX_MISSING,
-               NULL));
-    return;
-  }
-
-  if (pc->choice_index >= pc->choices_len)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Order `%s' has choices array with %u elements but "
-                "request has 'choice_index' field with value %ld\n",
-                pc->order_id,
-                pc->choices_len,
-                pc->choice_index);
-    GNUNET_break (0);
-    pay_end (pc,
-             TALER_MHD_reply_with_error (
-               pc->connection,
-               MHD_HTTP_BAD_REQUEST,
-               TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_CHOICE_INDEX_OUT_OF_BOUNDS,
-               NULL));
-    return;
-  }
-
-  {
-    const struct TALER_MerchantContractChoice *selected
-      = &pc->choices[pc->choice_index];
-
-    for (unsigned int i = 0; i<selected->inputs_len; i++)
+    pc->validate_tokens.max_fee = pc->details.v0.max_fee;
+    pc->validate_tokens.brutto = pc->details.v0.amount;
+    break;
+  case TALER_MCV_V1:
     {
-      const struct TALER_MerchantContractInput *input
-        = &selected->inputs[i];
-      const struct TALER_MerchantContractTokenFamily *family;
+      const struct TALER_MerchantContractChoice *selected
+        = &pc->details.v1.choices[pc->choice_index];
 
-      if (input->type != TALER_MCIT_TOKEN)
-      {
-        /* only validate inputs of type token (for now) */
-        continue;
-      }
+      pc->validate_tokens.max_fee = selected->max_fee;
+      pc->validate_tokens.brutto = selected->amount;
 
-      family = find_family (pc,
-                            input->details.token.token_family_slug);
-      if (NULL == family)
+      for (unsigned int i = 0; i<selected->inputs_len; i++)
       {
-        /* this should never happen, since the choices and
-           token families are validated on insert. */
-        GNUNET_break (0);
-        pay_end (pc,
-                 TALER_MHD_reply_with_error (
-                   pc->connection,
-                   MHD_HTTP_INTERNAL_SERVER_ERROR,
-                   TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
-                   "token family not found in order"));
-        return;
-      }
-      if (GNUNET_NO ==
-          find_valid_input_tokens (pc,
-                                   family,
-                                   i,
-                                   input->details.token.count))
-      {
-        /* Error is already scheduled from find_valid_input_token. */
-        return;
-      }
-    }
-
-    GNUNET_array_grow (pc->output_tokens,
-                       pc->output_tokens_len,
-                       selected->outputs_len);
+        const struct TALER_MerchantContractInput *input
+          = &selected->inputs[i];
+        const struct TALER_MerchantContractTokenFamily *family;
 
-    for (unsigned int i = 0; i<selected->outputs_len; i++)
-    {
-      enum GNUNET_DB_QueryStatus qs;
-      struct TALER_MERCHANTDB_TokenFamilyKeyDetails details;
-      const struct TALER_MerchantContractOutput *output
-        = &selected->outputs[i];
-      struct TALER_MerchantContractTokenFamily *family;
-      struct TALER_MerchantContractTokenFamilyKey *key;
+        if (input->type != TALER_MCIT_TOKEN)
+        {
+          /* only validate inputs of type token (for now) */
+          continue;
+        }
 
-      if (output->type != TALER_MCOT_TOKEN)
-      {
-        /* only validate outputs of type tokens (for now) */
-        continue;
+        family = find_family (pc,
+                              input->details.token.token_family_slug);
+        if (NULL == family)
+        {
+          /* this should never happen, since the choices and
+             token families are validated on insert. */
+          GNUNET_break (0);
+          pay_end (pc,
+                   TALER_MHD_reply_with_error (
+                     pc->connection,
+                     MHD_HTTP_INTERNAL_SERVER_ERROR,
+                     TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+                     "token family not found in order"));
+          return;
+        }
+        if (GNUNET_NO ==
+            find_valid_input_tokens (pc,
+                                     family,
+                                     i,
+                                     input->details.token.count))
+        {
+          /* Error is already scheduled from find_valid_input_token. */
+          return;
+        }
       }
 
-      family = find_family (pc,
-                            output->details.token.token_family_slug);
-      if (NULL == family)
-      {
-        /* this should never happen, since the choices and
-           token families are validated on insert. */
-        GNUNET_break (0);
-        pay_end (pc,
-                 TALER_MHD_reply_with_error (
-                   pc->connection,
-                   MHD_HTTP_INTERNAL_SERVER_ERROR,
-                   TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
-                   "token family not found in order"));
-        return;
-      }
-      if (output->details.token.key_index >= family->keys_len)
-      {
-        /* this should never happen, since the choices and
-           token families are validated on insert. */
-        GNUNET_break (0);
-        pay_end (pc,
-                 TALER_MHD_reply_with_error (
-                   pc->connection,
-                   MHD_HTTP_INTERNAL_SERVER_ERROR,
-                   TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
-                   "key index invalid for token family"));
-        return;
-      }
-      key = &family->keys[output->details.token.key_index];
-      qs = TMH_db->lookup_token_family_key (
-        TMH_db->cls,
-        pc->hc->instance->settings.id,
-        family->slug,
-        pc->timestamp,
-        pc->pay_deadline,
-        &details);
-      if (qs <= 0)
+      GNUNET_array_grow (pc->output_tokens,
+                         pc->output_tokens_len,
+                         selected->outputs_len);
+
+      for (unsigned int i = 0; i<selected->outputs_len; i++)
       {
-        GNUNET_log (
-          GNUNET_ERROR_TYPE_ERROR,
-          "Did not find key for %s at [%llu,%llu]\n",
-          family->slug,
-          (unsigned long long) pc->timestamp.abs_time.abs_value_us,
-          (unsigned long long) pc->pay_deadline.abs_time.abs_value_us);
-        GNUNET_break (0);
-        pay_end (pc,
-                 TALER_MHD_reply_with_error (
-                   pc->connection,
-                   MHD_HTTP_INTERNAL_SERVER_ERROR,
-                   TALER_EC_GENERIC_DB_FETCH_FAILED,
-                   NULL));
-        return;
-      }
+        enum GNUNET_DB_QueryStatus qs;
+        struct TALER_MERCHANTDB_TokenFamilyKeyDetails details;
+        const struct TALER_MerchantContractOutput *output
+          = &selected->outputs[i];
+        const struct TALER_MerchantContractTokenFamily *family;
+        struct TALER_MerchantContractTokenFamilyKey *key;
+
+        if (output->type != TALER_MCOT_TOKEN)
+        {
+          /* only validate outputs of type tokens (for now) */
+          continue;
+        }
 
-      GNUNET_assert (NULL != details.priv.private_key);
+        family = find_family (pc,
+                              output->details.token.token_family_slug);
+        if (NULL == family)
+        {
+          /* this should never happen, since the choices and
+             token families are validated on insert. */
+          GNUNET_break (0);
+          pay_end (pc,
+                   TALER_MHD_reply_with_error (
+                     pc->connection,
+                     MHD_HTTP_INTERNAL_SERVER_ERROR,
+                     TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+                     "token family not found in order"));
+          return;
+        }
+        if (output->details.token.key_index >= family->keys_len)
+        {
+          /* this should never happen, since the choices and
+             token families are validated on insert. */
+          GNUNET_break (0);
+          pay_end (pc,
+                   TALER_MHD_reply_with_error (
+                     pc->connection,
+                     MHD_HTTP_INTERNAL_SERVER_ERROR,
+                     TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+                     "key index invalid for token family"));
+          return;
+        }
+        key = &family->keys[output->details.token.key_index];
+        qs = TMH_db->lookup_token_family_key (
+          TMH_db->cls,
+          pc->hc->instance->settings.id,
+          family->slug,
+          pc->timestamp,
+          pc->pay_deadline,
+          &details);
+        if (qs <= 0)
+        {
+          GNUNET_log (
+            GNUNET_ERROR_TYPE_ERROR,
+            "Did not find key for %s at [%llu,%llu]\n",
+            family->slug,
+            (unsigned long long) pc->timestamp.abs_time.abs_value_us,
+            (unsigned long long) pc->pay_deadline.abs_time.abs_value_us);
+          GNUNET_break (0);
+          pay_end (pc,
+                   TALER_MHD_reply_with_error (
+                     pc->connection,
+                     MHD_HTTP_INTERNAL_SERVER_ERROR,
+                     TALER_EC_GENERIC_DB_FETCH_FAILED,
+                     NULL));
+          return;
+        }
 
-      if (GNUNET_OK !=
-          sign_token_envelopes (pc,
-                                key,
-                                &details.priv,
-                                /* TODO: Use critical field stored in database 
here instead. */
-                                details.token_family.kind ==
-                                TALER_MERCHANTDB_TFK_Subscription,
-                                i,
-                                output->details.token.count))
-      {
-        /* Error is already scheduled from sign_token_envelopes. */
-        return;
+        GNUNET_assert (NULL != details.priv.private_key);
+
+        if (GNUNET_OK !=
+            sign_token_envelopes (pc,
+                                  key,
+                                  &details.priv,
+                                  /* TODO: Use critical field stored in 
database here instead. */
+                                  details.token_family.kind ==
+                                  TALER_MERCHANTDB_TFK_Subscription,
+                                  i,
+                                  output->details.token.count))
+        {
+          /* Error is already scheduled from sign_token_envelopes. */
+          return;
+        }
       }
+    }
+  }
+
+  for (size_t i = 0; i<pc->coins_cnt; i++)
+  {
+    const struct DepositConfirmation *dc = &pc->dc[i];
 
+    if (GNUNET_OK !=
+        TALER_amount_cmp_currency (&dc->cdd.amount,
+                                   &pc->validate_tokens.brutto))
+    {
+      GNUNET_break_op (0);
+      fprintf (stderr,
+               "HERE (%u): %s != %s\n",
+               (unsigned int) pc->version,
+               dc->cdd.amount.currency,
+               TALER_amount2s (&pc->validate_tokens.brutto));
+      pay_end (pc,
+               TALER_MHD_reply_with_error (
+                 pc->connection,
+                 MHD_HTTP_CONFLICT,
+                 TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
+                 pc->validate_tokens.brutto.currency));
+      return;
     }
   }
 
@@ -3174,16 +3220,17 @@ phase_check_contract (struct PayContext *pc)
   /* Get details from contract and check fundamentals */
   {
     const char *fulfillment_url = NULL;
+    uint64_t version = 0;
     struct GNUNET_JSON_Specification espec[] = {
-      TALER_JSON_spec_amount_any ("amount",
-                                  &pc->amount),
+      GNUNET_JSON_spec_mark_optional (
+        GNUNET_JSON_spec_uint64 ("version",
+                                 &version),
+        NULL),
       GNUNET_JSON_spec_mark_optional (
         /* This one does not have to be a Web URL */
         GNUNET_JSON_spec_string ("fulfillment_url",
                                  &fulfillment_url),
         NULL),
-      TALER_JSON_spec_amount_any ("max_fee",
-                                  &pc->max_fee),
       GNUNET_JSON_spec_timestamp ("timestamp",
                                   &pc->timestamp),
       GNUNET_JSON_spec_timestamp ("refund_deadline",
@@ -3192,16 +3239,6 @@ phase_check_contract (struct PayContext *pc)
                                   &pc->pay_deadline),
       GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
                                   &pc->wire_transfer_deadline),
-      GNUNET_JSON_spec_mark_optional (
-        TALER_JSON_spec_choices ("choices",
-                                 &pc->choices,
-                                 &pc->choices_len),
-        NULL),
-      GNUNET_JSON_spec_mark_optional (
-        TALER_JSON_spec_token_families ("token_families",
-                                        &pc->token_families,
-                                        &pc->token_families_len),
-        NULL),
       GNUNET_JSON_spec_fixed_auto ("h_wire",
                                    &pc->h_wire),
       GNUNET_JSON_spec_mark_optional (
@@ -3216,8 +3253,6 @@ phase_check_contract (struct PayContext *pc)
     res = TALER_MHD_parse_internal_json_data (pc->connection,
                                               pc->contract_terms,
                                               espec);
-    if (NULL != fulfillment_url)
-      pc->fulfillment_url = GNUNET_strdup (fulfillment_url);
     if (GNUNET_YES != res)
     {
       GNUNET_break (0);
@@ -3227,41 +3262,131 @@ phase_check_contract (struct PayContext *pc)
                : MHD_NO);
       return;
     }
-  }
-
-  if (GNUNET_OK !=
-      TALER_amount_cmp_currency (&pc->max_fee,
-                                 &pc->amount))
-  {
-    GNUNET_break (0);
-    pay_end (pc,
-             TALER_MHD_reply_with_error (
-               pc->connection,
-               MHD_HTTP_INTERNAL_SERVER_ERROR,
-               TALER_EC_GENERIC_DB_FETCH_FAILED,
-               "'max_fee' in database does not match currency of contract 
price"));
-    return;
-  }
-
-  for (size_t i = 0; i<pc->coins_cnt; i++)
-  {
-    struct DepositConfirmation *dc = &pc->dc[i];
-
-    if (GNUNET_OK !=
-        TALER_amount_cmp_currency (&dc->cdd.amount,
-                                   &pc->amount))
+    if (NULL != fulfillment_url)
+      pc->fulfillment_url = GNUNET_strdup (fulfillment_url);
+    switch (version)
     {
-      GNUNET_break_op (0);
+    case 0:
+      {
+        struct GNUNET_JSON_Specification v0spec[] = {
+          TALER_JSON_spec_amount_any ("amount",
+                                      &pc->details.v0.amount),
+          TALER_JSON_spec_amount_any ("max_fee",
+                                      &pc->details.v0.max_fee),
+          GNUNET_JSON_spec_end ()
+        };
+        res = TALER_MHD_parse_internal_json_data (pc->connection,
+                                                  pc->contract_terms,
+                                                  v0spec);
+        if (GNUNET_YES != res)
+        {
+          GNUNET_break (0);
+          pay_end (pc,
+                   (GNUNET_NO == res)
+               ? MHD_YES
+               : MHD_NO);
+          return;
+        }
+        if (GNUNET_OK !=
+            TALER_amount_cmp_currency (&pc->details.v0.max_fee,
+                                       &pc->details.v0.amount))
+        {
+          GNUNET_break (0);
+          pay_end (pc,
+                   TALER_MHD_reply_with_error (
+                     pc->connection,
+                     MHD_HTTP_INTERNAL_SERVER_ERROR,
+                     TALER_EC_GENERIC_DB_FETCH_FAILED,
+                     "'max_fee' in database does not match currency of 
contract price"));
+          return;
+        }
+
+        if (pc->choice_index > 0)
+        {
+          GNUNET_break (0);
+          pay_end (pc,
+                   TALER_MHD_reply_with_error (
+                     pc->connection,
+                     MHD_HTTP_BAD_REQUEST,
+                     
TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_CHOICE_INDEX_OUT_OF_BOUNDS,
+                     "contract terms v0 has no choices"));
+          return;
+        }
+      }
+      pc->version = TALER_MCV_V0;
+      break;
+    case 1:
+      {
+        struct GNUNET_JSON_Specification v1spec[] = {
+          TALER_JSON_spec_choices ("choices",
+                                   &pc->details.v1.choices,
+                                   &pc->details.v1.choices_len),
+          TALER_JSON_spec_token_families ("token_families",
+                                          &pc->details.v1.token_families,
+                                          &pc->details.v1.token_families_len),
+          GNUNET_JSON_spec_end ()
+        };
+        res = TALER_MHD_parse_internal_json_data (pc->connection,
+                                                  pc->contract_terms,
+                                                  v1spec);
+        if (GNUNET_YES != res)
+        {
+          GNUNET_break (0);
+          pay_end (pc,
+                   (GNUNET_NO == res)
+               ? MHD_YES
+               : MHD_NO);
+          return;
+        }
+        if (pc->choice_index < 0)
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                      "Order `%s' has non-empty choices array but"
+                      "request is missing 'choice_index' field\n",
+                      pc->order_id);
+          GNUNET_break (0);
+          pay_end (pc,
+                   TALER_MHD_reply_with_error (
+                     pc->connection,
+                     MHD_HTTP_BAD_REQUEST,
+                     TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_CHOICE_INDEX_MISSING,
+                     NULL));
+          return;
+        }
+        if (pc->choice_index >= pc->details.v1.choices_len)
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                      "Order `%s' has choices array with %u elements but "
+                      "request has 'choice_index' field with value %ld\n",
+                      pc->order_id,
+                      pc->details.v1.choices_len,
+                      pc->choice_index);
+          GNUNET_break (0);
+          pay_end (pc,
+                   TALER_MHD_reply_with_error (
+                     pc->connection,
+                     MHD_HTTP_BAD_REQUEST,
+                     
TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_CHOICE_INDEX_OUT_OF_BOUNDS,
+                     NULL));
+          return;
+        }
+      }
+      pc->version = TALER_MCV_V1;
+      break;
+    default:
+      GNUNET_break (0);
       pay_end (pc,
                TALER_MHD_reply_with_error (
                  pc->connection,
-                 MHD_HTTP_CONFLICT,
-                 TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
-                 pc->amount.currency));
+                 MHD_HTTP_INTERNAL_SERVER_ERROR,
+                 TALER_EC_GENERIC_DB_FETCH_FAILED,
+                 "contract 'version' in database not supported by this 
backend")
+               );
       return;
     }
   }
 
+
   if (GNUNET_TIME_timestamp_cmp (pc->wire_transfer_deadline,
                                  <,
                                  pc->refund_deadline))
@@ -3288,7 +3413,7 @@ phase_check_contract (struct PayContext *pc)
     return;
   }
 
-  /* Make sure wire method (still) exists for this instance */
+/* Make sure wire method (still) exists for this instance */
   {
     struct TMH_WireMethod *wm;
 
@@ -3753,25 +3878,32 @@ pay_context_cleanup (void *cls)
     GNUNET_free (eg);
   }
   GNUNET_free (pc->egs);
-  for (unsigned int i = 0; i< pc->token_families_len; i++)
+  switch (pc->version)
   {
-    struct TALER_MerchantContractTokenFamily *tf
-      = &pc->token_families[i];
-
-    GNUNET_free (tf->slug);
-    GNUNET_free (tf->name);
-    GNUNET_free (tf->description);
-    json_decref (tf->description_i18n);
-    for (unsigned int j = 0; j<tf->keys_len; j++)
+  case TALER_MCV_V0:
+    break;
+  case TALER_MCV_V1:
+    for (unsigned int i = 0; i< pc->details.v1.token_families_len; i++)
     {
-      struct TALER_MerchantContractTokenFamilyKey *key
-        = &tf->keys[j];
+      struct TALER_MerchantContractTokenFamily *tf
+        = &pc->details.v1.token_families[i];
 
-      TALER_token_issue_pub_free (&key->pub);
+      GNUNET_free (tf->slug);
+      GNUNET_free (tf->name);
+      GNUNET_free (tf->description);
+      json_decref (tf->description_i18n);
+      for (unsigned int j = 0; j<tf->keys_len; j++)
+      {
+        struct TALER_MerchantContractTokenFamilyKey *key
+          = &tf->keys[j];
+
+        TALER_token_issue_pub_free (&key->pub);
+      }
+      GNUNET_free (tf->keys);
     }
-    GNUNET_free (tf->keys);
+    GNUNET_free (pc->details.v1.token_families);
+    break;
   }
-  GNUNET_free (pc->token_families);
   if (NULL != pc->response)
   {
     MHD_destroy_response (pc->response);
diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c 
b/src/backend/taler-merchant-httpd_private-post-orders.c
index 1cb11a15..98831226 100644
--- a/src/backend/taler-merchant-httpd_private-post-orders.c
+++ b/src/backend/taler-merchant-httpd_private-post-orders.c
@@ -2270,6 +2270,7 @@ compute_fee (struct OrderContext *oc,
     return;
   }
   if ( (settings->use_stefan) &&
+       (NULL != max_stefan_fee) &&
        (GNUNET_OK ==
         TALER_amount_is_valid (max_stefan_fee)) )
   {
@@ -2309,7 +2310,9 @@ set_max_fee (struct OrderContext *oc)
       compute_fee (oc,
                    &oc->parse_choices.choices[i].amount,
                    &oc->parse_choices.choices[i].max_fee,
-                   &oc->set_exchanges.details.v1.max_stefan_fees[i],
+                   NULL != oc->set_exchanges.details.v1.max_stefan_fees
+                   ? &oc->set_exchanges.details.v1.max_stefan_fees[i]
+                   : NULL,
                    &oc->set_max_fee.details.v1.max_fees[i]);
     break;
   default:
diff --git a/src/testing/testing_api_cmd_pay_order.c 
b/src/testing/testing_api_cmd_pay_order.c
index 49dd7e81..67ea3fdd 100644
--- a/src/testing/testing_api_cmd_pay_order.c
+++ b/src/testing/testing_api_cmd_pay_order.c
@@ -559,8 +559,6 @@ pay_run (void *cls,
   struct TALER_MerchantWireHashP h_wire;
   const struct TALER_PrivateContractHashP *h_proposal;
   struct TALER_Amount max_fee;
-  const json_t *choices = NULL;
-  const json_t *token_families = NULL;
   const char *error_name = NULL;
   unsigned int error_line = 0;
   struct TALER_MERCHANT_PayCoin *pay_coins;
@@ -598,7 +596,12 @@ pay_run (void *cls,
   {
     /* Get information that needs to be put verbatim in the
      * deposit permission */
+    uint64_t version = 0;
     struct GNUNET_JSON_Specification spec[] = {
+      GNUNET_JSON_spec_mark_optional (
+        GNUNET_JSON_spec_uint64 ("version",
+                                 &version),
+        NULL),
       GNUNET_JSON_spec_string ("order_id",
                                &order_id),
       GNUNET_JSON_spec_timestamp ("refund_deadline",
@@ -611,18 +614,6 @@ pay_run (void *cls,
                                    &merchant_pub),
       GNUNET_JSON_spec_fixed_auto ("h_wire",
                                    &h_wire),
-      TALER_JSON_spec_amount_any ("amount",
-                                  &ps->total_amount),
-      TALER_JSON_spec_amount_any ("max_fee",
-                                  &max_fee),
-      GNUNET_JSON_spec_mark_optional (
-        GNUNET_JSON_spec_object_const ("token_families",
-                                       &token_families),
-        NULL),
-      GNUNET_JSON_spec_mark_optional (
-        GNUNET_JSON_spec_array_const ("choices",
-                                      &choices),
-        NULL),
       /* FIXME oec: parse minimum age, use data later? */
       GNUNET_JSON_spec_end ()
     };
@@ -645,6 +636,221 @@ pay_run (void *cls,
       free (js);
       TALER_TESTING_FAIL (is);
     }
+    switch (version)
+    {
+    case 0:
+      {
+        struct GNUNET_JSON_Specification v0spec[] = {
+          TALER_JSON_spec_amount_any ("amount",
+                                      &ps->total_amount),
+          TALER_JSON_spec_amount_any ("max_fee",
+                                      &max_fee),
+          GNUNET_JSON_spec_end ()
+        };
+
+        if (GNUNET_OK !=
+            GNUNET_JSON_parse (contract_terms,
+                               v0spec,
+                               &error_name,
+                               &error_line))
+        {
+          char *js;
+
+          js = json_dumps (contract_terms,
+                           JSON_INDENT (1));
+          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                      "Parser failed on %s:%u for input `%s'\n",
+                      error_name,
+                      error_line,
+                      js);
+          free (js);
+          TALER_TESTING_FAIL (is);
+        }
+      }
+      if (0 < ps->choice_index)
+        TALER_TESTING_FAIL (is);
+      break;
+    case 1:
+      {
+        const json_t *choices;
+        const json_t *token_families;
+        struct GNUNET_JSON_Specification v1spec[] = {
+          GNUNET_JSON_spec_object_const ("token_families",
+                                         &token_families),
+          GNUNET_JSON_spec_array_const ("choices",
+                                        &choices),
+          GNUNET_JSON_spec_end ()
+        };
+        const json_t *outputs;
+        json_t *output;
+        unsigned int output_index;
+        const json_t *choice;
+
+        if (GNUNET_OK !=
+            GNUNET_JSON_parse (contract_terms,
+                               v1spec,
+                               &error_name,
+                               &error_line))
+        {
+          char *js;
+
+          js = json_dumps (contract_terms,
+                           JSON_INDENT (1));
+          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                      "Parser failed on %s:%u for input `%s'\n",
+                      error_name,
+                      error_line,
+                      js);
+          free (js);
+          TALER_TESTING_FAIL (is);
+        }
+
+        choice = json_array_get (choices,
+                                 ps->choice_index);
+        if (NULL == choice)
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                      "No choice found at index %d\n",
+                      ps->choice_index);
+          TALER_TESTING_FAIL (is);
+        }
+
+        {
+          const char *ierror_name = NULL;
+          unsigned int ierror_line = 0;
+
+          struct GNUNET_JSON_Specification ispec[] = {
+            TALER_JSON_spec_amount_any ("amount",
+                                        &ps->total_amount),
+            TALER_JSON_spec_amount_any ("max_fee",
+                                        &max_fee),
+            GNUNET_JSON_spec_array_const ("outputs",
+                                          &outputs),
+            GNUNET_JSON_spec_end ()
+          };
+
+          if (GNUNET_OK !=
+              GNUNET_JSON_parse (choice,
+                                 ispec,
+                                 &ierror_name,
+                                 &ierror_line))
+          {
+            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                        "Parser failed on %s:%u for input `%s'\n",
+                        ierror_name,
+                        ierror_line,
+                        json_dumps (choice,
+                                    JSON_INDENT (2)));
+            TALER_TESTING_FAIL (is);
+          }
+        }
+
+        json_array_foreach (outputs, output_index, output)
+        {
+          const char *slug;
+          const char *kind;
+          uint32_t key_index;
+          uint32_t count = 1;
+          const char *ierror_name = NULL;
+          unsigned int ierror_line = 0;
+
+          struct GNUNET_JSON_Specification ispec[] = {
+            GNUNET_JSON_spec_string ("kind",
+                                     &kind),
+            GNUNET_JSON_spec_string ("token_family_slug",
+                                     &slug),
+            GNUNET_JSON_spec_uint32 ("key_index",
+                                     &key_index),
+            GNUNET_JSON_spec_mark_optional (
+              GNUNET_JSON_spec_uint32 ("count",
+                                       &count),
+              NULL),
+            GNUNET_JSON_spec_end ()
+          };
+
+          if (GNUNET_OK !=
+              GNUNET_JSON_parse (output,
+                                 ispec,
+                                 &ierror_name,
+                                 &ierror_line))
+          {
+            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                        "Parser failed on %s:%u for input `%s'\n",
+                        ierror_name,
+                        ierror_line,
+                        json_dumps (output,
+                                    JSON_INDENT (2)));
+            TALER_TESTING_FAIL (is);
+          }
+
+          if (0 != strcmp ("token", kind))
+          {
+            continue;
+          }
+
+          GNUNET_array_grow (ps->issued_tokens,
+                             ps->num_issued_tokens,
+                             ps->num_issued_tokens + count);
+
+          for (unsigned int k = 0; k < count; k++)
+          {
+            struct TALER_MERCHANT_PrivateTokenDetails *details =
+              &ps->issued_tokens[ps->num_issued_tokens - count + k];
+
+            if (GNUNET_OK !=
+                find_token_public_key (token_families,
+                                       slug,
+                                       key_index,
+                                       &details->issue_pub))
+            {
+              TALER_TESTING_FAIL (is);
+            }
+
+            /* Only RSA is supported for now. */
+            GNUNET_assert (GNUNET_CRYPTO_BSA_RSA ==
+                           details->issue_pub.public_key->cipher);
+
+            TALER_token_blind_input_copy (&details->blinding_inputs,
+                                          
TALER_token_blind_input_rsa_singleton ()
+                                          );
+            /* TODO: Where to get details->blinding_inputs from? */
+            TALER_token_use_setup_random (&details->master);
+            TALER_token_use_setup_priv (&details->master,
+                                        &details->blinding_inputs,
+                                        &details->token_priv);
+            TALER_token_use_blinding_secret_create (&details->master,
+                                                    &details->blinding_inputs,
+                                                    &details->blinding_secret);
+            GNUNET_CRYPTO_eddsa_key_get_public 
(&details->token_priv.private_key
+                                                ,
+                                                
&details->token_pub.public_key);
+            GNUNET_CRYPTO_hash (&details->token_pub.public_key,
+                                sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
+                                &details->h_token_pub.hash);
+            details->envelope.blinded_pub = GNUNET_CRYPTO_message_blind_to_sign
+                                            (
+              details->issue_pub.public_key,
+              &details->blinding_secret,
+              NULL, /* TODO: Add session nonce to support CS tokens */
+              &details->h_token_pub.hash,
+              sizeof (details->h_token_pub.hash),
+              details->blinding_inputs.blinding_inputs);
+
+            if (NULL == details->envelope.blinded_pub)
+            {
+              GNUNET_break (0);
+              TALER_TESTING_FAIL (is);
+            }
+          }
+        }
+      }
+
+      break;
+    default:
+      TALER_TESTING_FAIL (is);
+    }
+
+
   }
 
   {
@@ -688,143 +894,6 @@ pay_run (void *cls,
     }
     GNUNET_free (tr);
   }
-  if (0 <= ps->choice_index)
-  {
-    const json_t *outputs;
-    json_t *output;
-    unsigned int output_index;
-    const json_t *choice = json_array_get (choices, ps->choice_index);
-
-    if (NULL == choice)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "No choice found at index %d\n",
-                  ps->choice_index);
-      TALER_TESTING_FAIL (is);
-    }
-
-    {
-      const char *ierror_name = NULL;
-      unsigned int ierror_line = 0;
-
-      struct GNUNET_JSON_Specification ispec[] = {
-        GNUNET_JSON_spec_array_const ("outputs",
-                                      &outputs),
-        GNUNET_JSON_spec_end ()
-      };
-
-      if (GNUNET_OK !=
-          GNUNET_JSON_parse (choice,
-                             ispec,
-                             &ierror_name,
-                             &ierror_line))
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                    "Parser failed on %s:%u for input `%s'\n",
-                    ierror_name,
-                    ierror_line,
-                    json_dumps (choice,
-                                JSON_INDENT (2)));
-        TALER_TESTING_FAIL (is);
-      }
-    }
-
-    json_array_foreach (outputs, output_index, output)
-    {
-      const char *slug;
-      const char *kind;
-      uint32_t key_index;
-      uint32_t count = 1;
-      const char *ierror_name = NULL;
-      unsigned int ierror_line = 0;
-
-      struct GNUNET_JSON_Specification ispec[] = {
-        GNUNET_JSON_spec_string ("kind",
-                                 &kind),
-        GNUNET_JSON_spec_string ("token_family_slug",
-                                 &slug),
-        GNUNET_JSON_spec_uint32 ("key_index",
-                                 &key_index),
-        GNUNET_JSON_spec_mark_optional (
-          GNUNET_JSON_spec_uint32 ("count",
-                                   &count),
-          NULL),
-        GNUNET_JSON_spec_end ()
-      };
-
-      if (GNUNET_OK !=
-          GNUNET_JSON_parse (output,
-                             ispec,
-                             &ierror_name,
-                             &ierror_line))
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                    "Parser failed on %s:%u for input `%s'\n",
-                    ierror_name,
-                    ierror_line,
-                    json_dumps (output,
-                                JSON_INDENT (2)));
-        TALER_TESTING_FAIL (is);
-      }
-
-      if (0 != strcmp ("token", kind))
-      {
-        continue;
-      }
-
-      GNUNET_array_grow (ps->issued_tokens,
-                         ps->num_issued_tokens,
-                         ps->num_issued_tokens + count);
-
-      for (unsigned int k = 0; k < count; k++)
-      {
-        struct TALER_MERCHANT_PrivateTokenDetails *details =
-          &ps->issued_tokens[ps->num_issued_tokens - count + k];
-
-        if (GNUNET_OK !=
-            find_token_public_key (token_families,
-                                   slug,
-                                   key_index,
-                                   &details->issue_pub))
-        {
-          TALER_TESTING_FAIL (is);
-        }
-
-        /* Only RSA is supported for now. */
-        GNUNET_assert (GNUNET_CRYPTO_BSA_RSA ==
-                       details->issue_pub.public_key->cipher);
-
-        TALER_token_blind_input_copy (&details->blinding_inputs,
-                                      TALER_token_blind_input_rsa_singleton 
());
-        /* TODO: Where to get details->blinding_inputs from? */
-        TALER_token_use_setup_random (&details->master);
-        TALER_token_use_setup_priv (&details->master,
-                                    &details->blinding_inputs,
-                                    &details->token_priv);
-        TALER_token_use_blinding_secret_create (&details->master,
-                                                &details->blinding_inputs,
-                                                &details->blinding_secret);
-        GNUNET_CRYPTO_eddsa_key_get_public (&details->token_priv.private_key,
-                                            &details->token_pub.public_key);
-        GNUNET_CRYPTO_hash (&details->token_pub.public_key,
-                            sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
-                            &details->h_token_pub.hash);
-        details->envelope.blinded_pub = GNUNET_CRYPTO_message_blind_to_sign (
-          details->issue_pub.public_key,
-          &details->blinding_secret,
-          NULL, /* TODO: Add session nonce to support CS tokens */
-          &details->h_token_pub.hash,
-          sizeof (details->h_token_pub.hash),
-          details->blinding_inputs.blinding_inputs);
-
-        if (NULL == details->envelope.blinded_pub)
-        {
-          GNUNET_break (0);
-          TALER_TESTING_FAIL (is);
-        }
-      }
-    }
-  }
 
   GNUNET_array_grow (output_tokens,
                      len_output_tokens,

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