gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [taler-merchant] 01/02: Implementing withdraw and proposal


From: gnunet
Subject: [GNUnet-SVN] [taler-merchant] 01/02: Implementing withdraw and proposal submission in the payments generator.
Date: Thu, 09 Mar 2017 15:45:37 +0100

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

marcello pushed a commit to branch master
in repository merchant.

commit 3fd46330b99591dc0afa26146becf4f4c5f2987d
Author: Marcello Stanisci <address@hidden>
AuthorDate: Thu Mar 9 15:30:49 2017 +0100

    Implementing withdraw and proposal submission in the
    payments generator.
---
 src/samples/generate_payments.c | 407 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 407 insertions(+)

diff --git a/src/samples/generate_payments.c b/src/samples/generate_payments.c
index c225720..d30c7b4 100644
--- a/src/samples/generate_payments.c
+++ b/src/samples/generate_payments.c
@@ -32,6 +32,8 @@
 
 #define BANK_URI "http://localhost:8083/";
 
+#define ORDER_MAX_SIZE 1000
+
 /**
  * Task run on timeout.
  */
@@ -165,6 +167,54 @@ struct Command
   {
 
     /**
+     * Information for a #OC_WITHDRAW_SIGN command.
+     */
+    struct
+    {
+
+      /**
+       * Which reserve should we withdraw from?
+       */
+      const char *reserve_reference;
+
+      /**
+       * String describing the denomination value we should withdraw.
+       * A corresponding denomination key must exist in the exchange's
+       * offerings.  Can be NULL if @e pk is set instead.
+       */
+      const char *amount;
+
+      /**
+       * If @e amount is NULL, this specifies the denomination key to
+       * use.  Otherwise, this will be set (by the interpreter) to the
+       * denomination PK matching @e amount.
+       */
+      const struct TALER_EXCHANGE_DenomPublicKey *pk;
+
+      /**
+       * Set (by the interpreter) to the exchange's signature over the
+       * coin's public key.
+       */
+      struct TALER_DenominationSignature sig;
+
+      /**
+       * Set (by the interpreter) to the coin's private key.
+       */
+      struct TALER_CoinSpendPrivateKeyP coin_priv;
+
+      /**
+       * Blinding key used for the operation.
+       */
+      struct TALER_DenominationBlindingKeyP blinding_key;
+
+      /**
+       * Withdraw handle (while operation is running).
+       */
+      struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh;
+
+    } reserve_withdraw;
+
+    /**
      * Information for a #OC_ADMIN_ADD_INCOMING command.
      */
     struct
@@ -205,6 +255,43 @@ struct Command
 
     } admin_add_incoming;
 
+    /**
+     * Information for an #OC_PROPOSAL command.
+     */
+    struct
+    {
+
+      /**
+       * The order.
+       * It's dynamically generated because we need different transaction_id
+       * for different merchant instances.
+       */
+      char order[ORDER_MAX_SIZE];
+
+      /**
+       * Handle to the active PUT /proposal operation, or NULL.
+       */
+      struct TALER_MERCHANT_ProposalOperation *po;
+
+      /**
+       * Full contract in JSON, set by the /contract operation.
+       * FIXME: verify in the code that this bit is actually proposal
+       * data and not the whole proposal.
+       */
+      json_t *proposal_data;
+
+      /**
+       * Proposal's signature.
+       */
+      struct TALER_MerchantSignatureP merchant_sig;
+
+      /**
+       * Proposal data's hashcode.
+       */
+      struct GNUNET_HashCode hash;
+
+    } proposal;
+
   } details;
 
 };
@@ -259,6 +346,56 @@ next_command (struct InterpreterState *is)
 }
 
 /**
+ * Callback that works PUT /proposal's output.
+ *
+ * @param cls closure
+ * @param http_status HTTP response code, 200 indicates success;
+ *                    0 if the backend's reply is bogus (fails to follow the 
protocol)
+ * @param ec taler-specific error code
+ * @param obj the full received JSON reply, or
+ *            error details if the request failed
+ * @param proposal_data the order + additional information provided by the
+ * backend, NULL on error.
+ * @param sig merchant's signature over the contract, NULL on error
+ * @param h_contract hash of the contract, NULL on error
+ */
+static void
+proposal_cb (void *cls,
+             unsigned int http_status,
+            enum TALER_ErrorCode ec,
+             const json_t *obj,
+             const json_t *proposal_data,
+             const struct TALER_MerchantSignatureP *sig,
+             const struct GNUNET_HashCode *hash)
+{
+  struct InterpreterState *is = cls;
+  struct Command *cmd = &is->commands[is->ip];
+
+  cmd->details.proposal.po = NULL;
+  switch (http_status)
+  {
+  case MHD_HTTP_OK:
+    cmd->details.proposal.proposal_data = json_incref ((json_t *) 
proposal_data);
+    cmd->details.proposal.merchant_sig = *sig;
+    cmd->details.proposal.hash = *hash;
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Hashed proposal, '%s'\n",
+                GNUNET_h2s (hash));
+    break;
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "unexpected status code from /proposal: %u. Step %u\n",
+                http_status,
+                is->ip);
+    json_dumpf (obj, stderr, 0);
+    GNUNET_break (0);
+    fail (is);
+    return;
+  }
+  next_command (is);
+}
+
+/**
  * Function called upon completion of our /admin/add/incoming request.
  *
  * @param cls closure with the interpreter state
@@ -318,6 +455,114 @@ find_command (const struct InterpreterState *is,
 }
 
 /**
+ * Function called upon completion of our /reserve/withdraw request.
+ *
+ * @param cls closure with the interpreter state
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
status request
+ *                    0 if the exchange's reply is bogus (fails to follow the 
protocol)
+ * @param ec taler-specific error code
+ * @param sig signature over the coin, NULL on error
+ * @param full_response full response from the exchange (for logging, in case 
of errors)
+ */
+static void
+reserve_withdraw_cb (void *cls,
+                     unsigned int http_status,
+                    enum TALER_ErrorCode ec,
+                     const struct TALER_DenominationSignature *sig,
+                     const json_t *full_response)
+{
+  struct InterpreterState *is = cls;
+  struct Command *cmd = &is->commands[is->ip];
+
+  cmd->details.reserve_withdraw.wsh = NULL;
+  if (cmd->expected_response_code != http_status)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u to command %s\n",
+                http_status,
+                cmd->label);
+    json_dumpf (full_response, stderr, 0);
+    GNUNET_break (0);
+    fail (is);
+    return;
+  }
+  switch (http_status)
+  {
+  case MHD_HTTP_OK:
+    if (NULL == sig)
+    {
+      GNUNET_break (0);
+      fail (is);
+      return;
+    }
+    cmd->details.reserve_withdraw.sig.rsa_signature
+      = GNUNET_CRYPTO_rsa_signature_dup (sig->rsa_signature);
+    break;
+  case MHD_HTTP_PAYMENT_REQUIRED:
+    /* nothing to check */
+    break;
+  default:
+    /* Unsupported status code (by test harness) */
+    GNUNET_break (0);
+    break;
+  }
+  next_command (is);
+}
+
+/**
+ * Find denomination key matching the given amount.
+ *
+ * @param keys array of keys to search
+ * @param amount coin value to look for
+ * @return NULL if no matching key was found
+ */
+static const struct TALER_EXCHANGE_DenomPublicKey *
+find_pk (const struct TALER_EXCHANGE_Keys *keys,
+         const struct TALER_Amount *amount)
+{
+  unsigned int i;
+  struct GNUNET_TIME_Absolute now;
+  struct TALER_EXCHANGE_DenomPublicKey *pk;
+  char *str;
+
+  now = GNUNET_TIME_absolute_get ();
+  for (i=0;i<keys->num_denom_keys;i++)
+  {
+    pk = &keys->denom_keys[i];
+    if ( (0 == TALER_amount_cmp (amount,
+                                 &pk->value)) &&
+         (now.abs_value_us >= pk->valid_from.abs_value_us) &&
+         (now.abs_value_us < pk->withdraw_valid_until.abs_value_us) )
+      return pk;
+  }
+  /* do 2nd pass to check if expiration times are to blame for failure */
+  str = TALER_amount_to_string (amount);
+  for (i=0;i<keys->num_denom_keys;i++)
+  {
+    pk = &keys->denom_keys[i];
+    if ( (0 == TALER_amount_cmp (amount,
+                                 &pk->value)) &&
+         ( (now.abs_value_us < pk->valid_from.abs_value_us) ||
+           (now.abs_value_us > pk->withdraw_valid_until.abs_value_us) ) )
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  "Have denomination key for `%s', but with wrong expiration 
range %llu vs [%llu,%llu)\n",
+                  str,
+                  (unsigned long long) now.abs_value_us,
+                  (unsigned long long) pk->valid_from.abs_value_us,
+                  (unsigned long long) pk->withdraw_valid_until.abs_value_us);
+      GNUNET_free (str);
+      return NULL;
+    }
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "No denomination key for amount %s found\n",
+              str);
+  GNUNET_free (str);
+  return NULL;
+}
+
+/**
  * Run the main interpreter loop that performs exchange operations.
  *
  * @param cls contains the `struct InterpreterState`
@@ -358,6 +603,41 @@ interpreter_run (void *cls)
       GNUNET_SCHEDULER_shutdown ();
       return;
 
+    case OC_PROPOSAL:
+      {
+        json_t *order;
+        json_error_t error;
+  
+        order = json_loads (cmd->details.proposal.order,
+                            JSON_REJECT_DUPLICATES,
+                            &error);
+        if (NULL == order)
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                      "Failed to parse the order `%s' at command #%u: %s at 
%u\n",
+                      cmd->details.proposal.order,
+                      is->ip,
+                      error.text,
+                      (unsigned int) error.column);
+          fail (is);
+          return;
+        }
+        cmd->details.proposal.po
+          = TALER_MERCHANT_order_put (ctx,
+                                      MERCHANT_URI,
+                                      order,
+                                      &proposal_cb,
+                                      is);
+        json_decref (order);
+        if (NULL == cmd->details.proposal.po)
+        {
+          GNUNET_break (0);
+          fail (is);
+          return;
+        }
+        return;
+      }
+
     case OC_ADMIN_ADD_INCOMING:
       if (NULL !=
           cmd->details.admin_add_incoming.reserve_reference)
@@ -519,6 +799,74 @@ interpreter_run (void *cls)
 
       return;
 
+    case OC_WITHDRAW_SIGN:
+      GNUNET_assert (NULL !=
+                     cmd->details.reserve_withdraw.reserve_reference);
+      ref = find_command (is,
+                          cmd->details.reserve_withdraw.reserve_reference);
+      GNUNET_assert (NULL != ref);
+      GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc);
+      if (NULL != cmd->details.reserve_withdraw.amount)
+      {
+        if (GNUNET_OK !=
+            TALER_string_to_amount (cmd->details.reserve_withdraw.amount,
+                                    &amount))
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                      "Failed to parse amount `%s' at %u\n",
+                      cmd->details.reserve_withdraw.amount,
+                      is->ip);
+          fail (is);
+          return;
+        }
+        cmd->details.reserve_withdraw.pk = find_pk (is->keys,
+                                                    &amount);
+      }
+      if (NULL == cmd->details.reserve_withdraw.pk)
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    "Failed to determine denomination key at %u\n",
+                    is->ip);
+        fail (is);
+        return;
+      }
+  
+      /* create coin's private key */
+      {
+        struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
+  
+        priv = GNUNET_CRYPTO_eddsa_key_create ();
+        cmd->details.reserve_withdraw.coin_priv.eddsa_priv = *priv;
+        GNUNET_free (priv);
+      }
+      GNUNET_CRYPTO_eddsa_key_get_public 
(&cmd->details.reserve_withdraw.coin_priv.eddsa_priv,
+                                          &coin_pub.eddsa_pub);
+      GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+                                  &cmd->details.reserve_withdraw.blinding_key,
+                                  sizeof 
(cmd->details.reserve_withdraw.blinding_key));
+  
+      cmd->details.reserve_withdraw.wsh
+        = TALER_EXCHANGE_reserve_withdraw (exchange,
+                                           cmd->details.reserve_withdraw.pk,
+                                           
&ref->details.admin_add_incoming.reserve_priv,
+                                           
&cmd->details.reserve_withdraw.coin_priv,
+                                           
&cmd->details.reserve_withdraw.blinding_key,
+                                           &reserve_withdraw_cb,
+                                           is);
+      if (NULL == cmd->details.reserve_withdraw.wsh)
+      {
+        GNUNET_break (0);
+        fail (is);
+        return;
+      }
+      return;
+
+    default:
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  "Unknown command, OC: %d, label: %s.\n",
+                  cmd->oc,
+                  cmd->label);
+      fail (is);
   }
 }
 
@@ -599,6 +947,41 @@ do_shutdown (void *cls)
     case OC_END:
       GNUNET_assert (0);
       break;
+
+    case OC_PROPOSAL:
+      if (NULL != cmd->details.proposal.po)
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    "Command %u (%s) did not complete\n",
+                    i,
+                    cmd->label);
+        TALER_MERCHANT_proposal_cancel (cmd->details.proposal.po);
+        cmd->details.proposal.po = NULL;
+      }
+      if (NULL != cmd->details.proposal.proposal_data)
+      {
+        json_decref (cmd->details.proposal.proposal_data);
+        cmd->details.proposal.proposal_data = NULL;
+      }
+      break;
+
+    case OC_WITHDRAW_SIGN:
+      if (NULL != cmd->details.reserve_withdraw.wsh)
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    "Command %u (%s) did not complete\n",
+                    i,
+                    cmd->label);
+        TALER_EXCHANGE_reserve_withdraw_cancel 
(cmd->details.reserve_withdraw.wsh);
+        cmd->details.reserve_withdraw.wsh = NULL;
+      }
+      if (NULL != cmd->details.reserve_withdraw.sig.rsa_signature)
+      {
+        GNUNET_CRYPTO_rsa_signature_free 
(cmd->details.reserve_withdraw.sig.rsa_signature);
+        cmd->details.reserve_withdraw.sig.rsa_signature = NULL;
+      }
+      break;
+
     case OC_ADMIN_ADD_INCOMING:
       if (NULL != cmd->details.admin_add_incoming.aih)
       {
@@ -663,6 +1046,30 @@ run (void *cls)
       .details.admin_add_incoming.sender_details = "{ \"type\":\"test\", 
\"bank_uri\":\"" BANK_URI "\", \"account_number\":62, \"uuid\":1 }",
       .details.admin_add_incoming.transfer_details = "{ \"uuid\": 1}",
       .details.admin_add_incoming.amount = "EUR:5.01" },
+    /* Withdraw a 5 EUR coin, at fee of 1 ct */
+    { .oc = OC_WITHDRAW_SIGN,
+      .label = "withdraw-coin-1",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.reserve_withdraw.reserve_reference = "create-reserve-1",
+      .details.reserve_withdraw.amount = "EUR:5" },
+
+    /* Create proposal */
+    { .oc = OC_PROPOSAL,
+      .label = "create-proposal-1",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.proposal.order = "{\
+                  \"max_fee\":\
+                     {\"currency\":\"EUR\", \"value\":0, 
\"fraction\":50000000},\
+                  \"order_id\":\"1\",\
+                  \"timestamp\":\"\\/Date(42)\\/\",\
+                  \"refund_deadline\":\"\\/Date(0)\\/\",\
+                  \"pay_deadline\":\"\\/Date(9999999999)\\/\",\
+                  \"amount\":{\"currency\":\"EUR\", \"value\":5, 
\"fraction\":0},\
+                  \"merchant\":{\"instance\":\"default\"},\
+                 \"summary\": \"merchant-lib testcase\",\
+                  \"products\":\
+                     [ {\"description\":\"ice cream\", \"value\":\"{EUR:5}\"} 
] }"},
+
     { .oc = OC_END,
       .label = "end-of-commands"}
   };

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



reply via email to

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