gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] branch master updated (e2deb89a -> 20cd46f6)


From: gnunet
Subject: [taler-exchange] branch master updated (e2deb89a -> 20cd46f6)
Date: Mon, 06 Mar 2023 20:51:25 +0100

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

oec pushed a change to branch master
in repository exchange.

    from e2deb89a -fix missing /home/grothoff for taler-exchange-offline user
     new af1001bc -cleanup age-withdraw_reveal
     new 262b4708 WiP: age-withdraw implementation, part 3/n
     new 2c78cb71 -fix missing /home/grothoff for taler-exchange-offline user
     new 20cd46f6 Merge branch 'age-withdraw'

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 contrib/gana                                       |   2 +-
 .../taler-exchange-httpd_age-withdraw_reveal.c     | 402 ++++++++++++++++-----
 2 files changed, 316 insertions(+), 88 deletions(-)

diff --git a/contrib/gana b/contrib/gana
index 214bc664..02132ede 160000
--- a/contrib/gana
+++ b/contrib/gana
@@ -1 +1 @@
-Subproject commit 214bc664476333a2c042ae57911558d1325e725f
+Subproject commit 02132ededc12a0a1cfd81f0ca76c384304e15259
diff --git a/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c 
b/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c
index 65bbb432..daefca4c 100644
--- a/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c
+++ b/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c
@@ -42,7 +42,7 @@ struct AgeRevealContext
   /**
    * Public key of the reserve for with the age-withdraw commitment was
    * originally made.  This parameter is provided by the client again
-   * during the call to reveal in order to save a database-lookup .
+   * during the call to reveal in order to save a database-lookup.
    */
   struct TALER_ReservePublicKeyP reserve_pub;
 
@@ -52,20 +52,41 @@ struct AgeRevealContext
   uint32_t num_coins;
 
   /**
-   * TODO:oec num_coins denoms
+   * #num_coins hashes of the denominations from which the coins are withdrawn.
+   * Those must support age restriction.
    */
   struct TALER_DenominationHashP *denoms_h;
 
   /**
-   * TODO:oec num_coins blinded coins
+   * #num_coins denomination keys, found in the system, according to denoms_h;
+   */
+  struct TEH_DenominationKey *denom_keys;
+
+  /**
+   * #num_coins hases of blinded coins.
    */
   struct TALER_BlindedCoinHashP *coin_evs;
 
   /**
-   * TODO:oec num_coins*(kappa - 1) disclosed coins
+   * Total sum of all denominations' values
+   **/
+  struct TALER_Amount total_amount;
+
+  /**
+   * Total sum of all denominations' fees
+   */
+  struct TALER_Amount total_fee;
+
+  /**
+   * #num_coins*(kappa - 1) disclosed coins.
    */
   struct GNUNET_CRYPTO_EddsaPrivateKey *disclosed_coins;
 
+  /**
+   * The data from the original age-withdraw.  Will be retrieved from
+   * the DB via @a ach.
+   */
+  struct TALER_EXCHANGEDB_AgeWithdrawCommitment commitment;
 };
 
 /**
@@ -75,30 +96,35 @@ void
 age_reveal_context_free (struct AgeRevealContext *actx)
 {
   GNUNET_free (actx->denoms_h);
+  GNUNET_free (actx->denom_keys);
   GNUNET_free (actx->coin_evs);
   GNUNET_free (actx->disclosed_coins);
 }
 
 
 /**
- * Handle a "/age-withdraw/$ACH/reveal request.  Parses the given JSON
- * ... TODO:oec:description
+ * Parse the json body of an '/age-withdraw/$ACH/reveal' request.  It extracts
+ * the denomination hashes, blinded coins and disclosed coins and allocates
+ * memory for those.
  *
  * @param connection The MHD connection to handle
- * @param actx The context of the operation, only partially built at call time
  * @param j_denoms_h Array of hashes of the denominations for the withdrawal, 
in JSON format
  * @param j_coin_evs The blinded envelopes in JSON format for the coins that 
are not revealed and will be signed on success
  * @param j_disclosed_coins The n*(kappa-1) disclosed coins' private keys in 
JSON format, from which all other attributes (age restriction, blinding, nonce) 
will be derived from
+ * @param[out] actx The context of the operation, only partially built at call 
time
+ * @param[out] mhd_mret The result if a reply is queued for MHD
+ * @return true on success, false on failure, with a reply already queued for 
MHD.
  */
-MHD_RESULT
-handle_age_withdraw_reveal_json (
+static enum GNUNET_GenericReturnValue
+parse_age_withdraw_reveal_json (
   struct MHD_Connection *connection,
-  struct AgeRevealContext *actx,
   const json_t *j_denoms_h,
   const json_t *j_coin_evs,
-  const json_t *j_disclosed_coins)
+  const json_t *j_disclosed_coins,
+  struct AgeRevealContext *actx,
+  MHD_RESULT *mhd_ret)
 {
-  MHD_RESULT mhd_ret = MHD_NO;
+  enum GNUNET_GenericReturnValue result = GNUNET_SYSERR;
 
   /* Verify JSON-structure consistency */
   {
@@ -116,135 +142,309 @@ handle_age_withdraw_reveal_json (
       error = "denoms_h must not be empty";
     else if (actx->num_coins != json_array_size (j_coin_evs))
       error = "denoms_h and coins_evs must be arrays of the same size";
-    else if (actx->num_coins * (TALER_CNC_KAPPA - 1)
-             != json_array_size (j_disclosed_coins))
-      error = "the size of array disclosed_coins must be "
-              TALER_CNC_KAPPA_MINUS_ONE_STR " times of the size of denoms_h";
     else if (actx->num_coins > TALER_MAX_FRESH_COINS)
       /**
-       * FIXME?: If the user had commited to more than the maximum coins 
allowed,
-       * the reserve has been charged, but now the user can not withdraw any 
money
-       * from it. How can the user get their money back?
+       * The wallet had committed to more than the maximum coins allowed, the
+       * reserve has been charged, but now the user can not withdraw any money
+       * from it.  Note that the user can't get their money back in this case!
        **/
       error = "maximum number of coins that can be withdrawn has been 
exceeded";
+    else if (actx->num_coins * (TALER_CNC_KAPPA - 1)
+             != json_array_size (j_disclosed_coins))
+      error = "the size of array disclosed_coins must be "
+              TALER_CNC_KAPPA_MINUS_ONE_STR " times the size of denoms_h";
 
     if (NULL != error)
-      return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_BAD_REQUEST,
-                                         TALER_EC_GENERIC_PARAMETER_MALFORMED,
-                                         error);
+    {
+      *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                             MHD_HTTP_BAD_REQUEST,
+                                             
TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                                             error);
+      return GNUNET_SYSERR;
+    }
   }
 
-  /* Parse denomination keys */
+  /* Continue parsing the parts */
   {
-    unsigned int idx;
-    json_t *jh;
+    unsigned int idx = 0;
+    json_t *value = NULL;
 
+    /* Parse denomination keys */
     actx->denoms_h = GNUNET_new_array (actx->num_coins,
                                        struct TALER_DenominationHashP);
 
-    json_array_foreach (j_denoms_h, idx, jh) {
+    json_array_foreach (j_denoms_h, idx, value) {
       struct GNUNET_JSON_Specification spec[] = {
         GNUNET_JSON_spec_fixed_auto (NULL, &actx->denoms_h[idx]),
         GNUNET_JSON_spec_end ()
       };
 
       if (GNUNET_OK !=
-          GNUNET_JSON_parse (jh, spec, NULL, NULL))
+          GNUNET_JSON_parse (value, spec, NULL, NULL))
       {
         char msg[256] = {0};
         GNUNET_snprintf (msg,
                          sizeof(msg),
                          "couldn't parse entry no. %d in array denoms_h",
                          idx + 1);
-        mhd_ret = TALER_MHD_reply_with_error (connection,
-                                              MHD_HTTP_BAD_REQUEST,
-                                              
TALER_EC_GENERIC_PARAMETER_MALFORMED,
-                                              msg);
+        *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                               MHD_HTTP_BAD_REQUEST,
+                                               
TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                                               msg);
         goto EXIT;
       }
-
     };
-  }
-
-  /* Parse blinded envelopes */
-  {
-    unsigned int idx;
-    json_t *ce;
 
+    /* Parse blinded envelopes */
     actx->coin_evs = GNUNET_new_array (actx->num_coins,
                                        struct TALER_BlindedCoinHashP);
 
-    json_array_foreach (j_coin_evs, idx, ce) {
+    json_array_foreach (j_coin_evs, idx, value) {
       struct GNUNET_JSON_Specification spec[] = {
         GNUNET_JSON_spec_fixed_auto (NULL, &actx->coin_evs[idx]),
         GNUNET_JSON_spec_end ()
       };
 
       if (GNUNET_OK !=
-          GNUNET_JSON_parse (ce, spec, NULL, NULL))
+          GNUNET_JSON_parse (value, spec, NULL, NULL))
       {
         char msg[256] = {0};
         GNUNET_snprintf (msg,
                          sizeof(msg),
                          "couldn't parse entry no. %d in array coin_evs",
                          idx + 1);
-        mhd_ret = TALER_MHD_reply_with_error (connection,
-                                              MHD_HTTP_BAD_REQUEST,
-                                              
TALER_EC_GENERIC_PARAMETER_MALFORMED,
-                                              msg);
+        *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                               MHD_HTTP_BAD_REQUEST,
+                                               
TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                                               msg);
         goto EXIT;
       }
     };
-  }
-
-  /* Parse diclosed keys */
-  {
-    unsigned int idx;
-    json_t *dc;
 
+    /* Parse diclosed keys */
     actx->disclosed_coins = GNUNET_new_array (
-      actx->num_coins * (TALER_CNC_KAPPA),
+      actx->num_coins * (TALER_CNC_KAPPA - 1),
       struct GNUNET_CRYPTO_EddsaPrivateKey);
 
-    json_array_foreach (j_coin_evs, idx, dc) {
+    json_array_foreach (j_disclosed_coins, idx, value) {
       struct GNUNET_JSON_Specification spec[] = {
         GNUNET_JSON_spec_fixed_auto (NULL, &actx->disclosed_coins[idx]),
         GNUNET_JSON_spec_end ()
       };
 
       if (GNUNET_OK !=
-          GNUNET_JSON_parse (dc, spec, NULL, NULL))
+          GNUNET_JSON_parse (value, spec, NULL, NULL))
       {
         char msg[256] = {0};
         GNUNET_snprintf (msg,
                          sizeof(msg),
                          "couldn't parse entry no. %d in array 
disclosed_coins",
                          idx + 1);
-        mhd_ret = TALER_MHD_reply_with_error (connection,
-                                              MHD_HTTP_BAD_REQUEST,
-                                              
TALER_EC_GENERIC_PARAMETER_MALFORMED,
-                                              msg);
+        *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                               MHD_HTTP_BAD_REQUEST,
+                                               
TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                                               msg);
         goto EXIT;
       }
     };
-
   }
 
-  /* TODO:oec: find commitment */
-  /* TODO:oec: check validity of denoms */
-  /* TODO:oec: check amount total against denoms */
-  /* TODO:oec: compute the disclosed blinded coins */
-  /* TODO:oec: generate h_commitment_comp */
-  /* TODO:oec: compare h_commitment_comp against h_commitment */
-  /* TODO:oec: sign the coins */
-  /* TODO:oec: send response */
+  result = GNUNET_OK;
+  *mhd_ret = MHD_YES;
 
 
-  /* TODO */
 EXIT:
-  age_reveal_context_free (actx);
-  return mhd_ret;
+  return result;
+}
+
+
+/**
+ * Check if the request belongs to an existing age-withdraw request.
+ * If so, sets the age_withdraw object with the request data.
+ * Otherwise, it queues an appropriate MHD response.
+ *
+ * @param connection The HTTP connection to the client
+ * @param h_commitment Original commitment value sent with the age-withdraw 
request
+ * @param reserve_pub Reserve public key used in the original age-withdraw 
request
+ * @param[out] commitment Data from the original age-withdraw request
+ * @param[out] result In the error cases, a response will be queued with MHD 
and this will be the result.
+ * @return GNUNET_OK if the withdraw request has been found,
+ *   GNUNET_SYSERROR if we did not find the request in the DB
+ */
+static enum GNUNET_GenericReturnValue
+retrieve_original_commitment (
+  struct MHD_Connection *connection,
+  const struct TALER_AgeWithdrawCommitmentHashP *h_commitment,
+  const struct TALER_ReservePublicKeyP *reserve_pub,
+  struct TALER_EXCHANGEDB_AgeWithdrawCommitment *commitment,
+  MHD_RESULT *result)
+{
+  enum GNUNET_DB_QueryStatus qs;
+
+  qs = TEH_plugin->get_age_withdraw_info (TEH_plugin->cls,
+                                          reserve_pub,
+                                          h_commitment,
+                                          commitment);
+  switch (qs)
+  {
+  case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+    return GNUNET_OK; /* Only happy case */
+
+  case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+    *result = TALER_MHD_reply_with_error (connection,
+                                          MHD_HTTP_NOT_FOUND,
+                                          
TALER_EC_EXCHANGE_AGE_WITHDRAW_COMMITMENT_UNKNOWN,
+                                          NULL);
+    break;
+
+  case GNUNET_DB_STATUS_HARD_ERROR:
+    *result = TALER_MHD_reply_with_error (connection,
+                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                          "get_age_withdraw_info");
+    break;
+
+  case GNUNET_DB_STATUS_SOFT_ERROR:
+  /* FIXME: Do we queue a result in this case or retry? */
+  default:
+    GNUNET_break (0);       /* should be impossible */
+    *result = TALER_MHD_reply_with_error (connection,
+                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                          
TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+                                          NULL);
+  }
+
+  return GNUNET_SYSERR;
+}
+
+
+/**
+ * Check if the given array of hashes of denomination_keys a) belong
+ * to valid denominations and b) those are marked as age restricted.
+ *
+ * @param connection The HTTP connection to the client
+ * @param len The lengths of the array @a denoms_h
+ * @param denoms_h array of hashes of denomination public keys
+ * @param[out] dks On success, will be filled with the denomination keys.  
Caller must deallocate.
+ * @param amount_with_fee The commited amount including fees
+ * @param[out] total_sum On success, will contain the total sum of all 
denominations
+ * @param[out] total_fee On success, will contain the total sum of all fees
+ * @param[out] result In the error cases, a response will be queued with MHD 
and this will be the result.
+ * @return GNUNET_OK if the denominations are valid and support age-restriction
+ *   GNUNET_SYSERR otherwise
+ */
+static enum GNUNET_GenericReturnValue
+all_denominations_valid (
+  struct MHD_Connection *connection,
+  uint32_t len,
+  const struct TALER_DenominationHashP *denoms_h,
+  struct TEH_DenominationKey **dks,
+  const struct TALER_Amount *amount_with_fee,
+  struct TALER_Amount *total_amount,
+  struct TALER_Amount *total_fee,
+  MHD_RESULT *result)
+{
+  struct TEH_KeyStateHandle *ksh;
+
+  GNUNET_assert (*dks == NULL);
+
+  ksh = TEH_keys_get_state ();
+  if (NULL == ksh)
+  {
+    *result = TALER_MHD_reply_with_error (connection,
+                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                          
TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
+                                          NULL);
+    return GNUNET_SYSERR;
+  }
+
+  *dks = GNUNET_new_array (len, struct TEH_DenominationKey);
+  TALER_amount_set_zero (TEH_currency, total_amount);
+  TALER_amount_set_zero (TEH_currency, total_fee);
+
+  for (uint32_t i = 0; i < len; i++)
+  {
+    dks[i] = TEH_keys_denomination_by_hash2 (
+      ksh,
+      &denoms_h[i],
+      connection,
+      result);
+
+    /* Does the denomination exist? */
+    if (NULL == dks[i])
+    {
+      GNUNET_assert (result != NULL);
+      /* Note: a HTTP-response has been queued and result has been set by
+       * TEH_keys_denominations_by_hash2 */
+      return GNUNET_SYSERR;
+    }
+
+    /* Does the denomation support age restriction ? */
+    if (0 == dks[i]->denom_pub.age_mask.bits)
+    {
+      char msg[256] = {0};
+      GNUNET_snprintf (msg,
+                       sizeof(msg),
+                       "denomination key no. %d does not support age 
restriction",
+                       i + 1);
+
+      *result = TALER_MHD_reply_with_error (connection,
+                                            MHD_HTTP_BAD_REQUEST,
+                                            
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN,
+                                            msg);
+      return GNUNET_SYSERR;
+    }
+
+    /* Accumulate the values */
+    if (0 > TALER_amount_add (
+          total_amount,
+          total_amount,
+          &dks[i]->meta.value))
+    {
+      GNUNET_break (0);
+      *result = TALER_MHD_reply_with_error (connection,
+                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                            
TALER_EC_EXCHANGE_AGE_WITHDRAW_AMOUNT_OVERFLOW,
+                                            "amount");
+      return GNUNET_SYSERR;
+    }
+
+    /* Accumulate the withdraw fees */
+    if (0 > TALER_amount_add (
+          total_fee,
+          total_fee,
+          &dks[i]->meta.fees.withdraw))
+    {
+      GNUNET_break (0);
+      *result = TALER_MHD_reply_with_error (connection,
+                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                            
TALER_EC_EXCHANGE_AGE_WITHDRAW_AMOUNT_OVERFLOW,
+                                            "fee");
+      return GNUNET_SYSERR;
+    }
+  }
+
+  /* Compare the commited amount against the totals */
+  {
+    struct TALER_Amount sum;
+    TALER_amount_set_zero (TEH_currency, &sum);
+
+    GNUNET_assert (0 < TALER_amount_add (
+                     &sum,
+                     total_amount,
+                     total_fee));
+
+    if (0 != TALER_amount_cmp (&sum, amount_with_fee))
+    {
+      GNUNET_break (0);
+      *result = TALER_MHD_reply_with_ec (connection,
+                                         
TALER_EC_EXCHANGE_AGE_WITHDRAW_AMOUNT_INCORRECT,
+                                         NULL);
+      return GNUNET_SYSERR;
+    }
+  }
+
+  return GNUNET_OK;
 }
 
 
@@ -254,6 +454,8 @@ TEH_handler_age_withdraw_reveal (
   const struct TALER_AgeWithdrawCommitmentHashP *ach,
   const json_t *root)
 {
+  MHD_RESULT result = MHD_NO;
+  enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
   struct AgeRevealContext actx = {0};
   json_t *j_denoms_h;
   json_t *j_coin_evs;
@@ -270,33 +472,59 @@ TEH_handler_age_withdraw_reveal (
 
   /* Parse JSON body*/
   {
-    enum GNUNET_GenericReturnValue res;
-
-    res = TALER_MHD_parse_json_data (rc->connection,
+    ret = TALER_MHD_parse_json_data (rc->connection,
                                      root,
                                      spec);
-    if (GNUNET_OK != res)
+    if (GNUNET_OK != ret)
     {
       GNUNET_break_op (0);
-      return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+      return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
     }
   }
 
 
-  /* handle reveal request */
-  {
-    MHD_RESULT res;
-
-    res = handle_age_withdraw_reveal_json (rc->connection,
-                                           &actx,
-                                           j_denoms_h,
-                                           j_coin_evs,
-                                           j_disclosed_coins);
+  do {
+    /* Extract denominations, blinded and disclosed coins */
+    if (GNUNET_OK != parse_age_withdraw_reveal_json (rc->connection,
+                                                     j_denoms_h,
+                                                     j_coin_evs,
+                                                     j_disclosed_coins,
+                                                     &actx,
+                                                     &result))
+      break;
+
+    /* Find original commitment */
+    if (GNUNET_OK != retrieve_original_commitment (rc->connection,
+                                                   &actx.ach,
+                                                   &actx.reserve_pub,
+                                                   &actx.commitment,
+                                                   &result))
+      break;
+
+    /* Ensure validity of denoms and the sum of amounts and fees */
+    if (GNUNET_OK != all_denominations_valid (
+          rc->connection,
+          actx.num_coins,
+          actx.denoms_h,
+          &actx.denom_keys,
+          &actx.commitment.amount_with_fee,
+          &actx.total_amount,
+          &actx.total_fee,
+          &result))
+      break;
+
+
+  } while(0);
 
-    GNUNET_JSON_parse_free (spec);
-    return res;
-  }
+  /* TODO:oec: compute the disclosed blinded coins */
+  /* TODO:oec: generate h_commitment_comp */
+  /* TODO:oec: compare h_commitment_comp against h_commitment */
+  /* TODO:oec: sign the coins */
+  /* TODO:oec: send response */
 
+  age_reveal_context_free (&actx);
+  GNUNET_JSON_parse_free (spec);
+  return result;
 }
 
 

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