gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] branch master updated: add purses-get to build


From: gnunet
Subject: [taler-exchange] branch master updated: add purses-get to build
Date: Sat, 23 Apr 2022 12:34:51 +0200

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

grothoff pushed a commit to branch master
in repository exchange.

The following commit(s) were added to refs/heads/master by this push:
     new 132359a4 add purses-get to build
132359a4 is described below

commit 132359a4440e07177df4afe596be4b16270a47d8
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Sat Apr 23 12:34:48 2022 +0200

    add purses-get to build
---
 contrib/gana                                   |   2 +-
 src/exchange/Makefile.am                       |   1 +
 src/exchange/taler-exchange-httpd_purses_get.c | 371 ++++++++++++++++++++++---
 src/exchange/taler-exchange-httpd_purses_get.h |  51 ++++
 src/include/taler_exchange_service.h           |   8 +-
 src/include/taler_exchangedb_plugin.h          |  18 ++
 src/lib/exchange_api_purses_get.c              |  32 ++-
 7 files changed, 426 insertions(+), 57 deletions(-)

diff --git a/contrib/gana b/contrib/gana
index c2580e60..bffe3241 160000
--- a/contrib/gana
+++ b/contrib/gana
@@ -1 +1 @@
-Subproject commit c2580e60259ba3aea2e69ea9da43482008b90d7c
+Subproject commit bffe32411e8ded537c5615ea054b43b3f7334bcd
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index b698753c..e4d037e3 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -106,6 +106,7 @@ taler_exchange_httpd_SOURCES = \
   taler-exchange-httpd_mhd.c taler-exchange-httpd_mhd.h \
   taler-exchange-httpd_purses_create.c taler-exchange-httpd_purses_create.h \
   taler-exchange-httpd_purses_deposit.c taler-exchange-httpd_purses_deposit.h \
+  taler-exchange-httpd_purses_get.c taler-exchange-httpd_purses_get.h \
   taler-exchange-httpd_purses_merge.c taler-exchange-httpd_purses_merge.h \
   taler-exchange-httpd_recoup.c taler-exchange-httpd_recoup.h \
   taler-exchange-httpd_recoup-refresh.c taler-exchange-httpd_recoup-refresh.h \
diff --git a/src/exchange/taler-exchange-httpd_purses_get.c 
b/src/exchange/taler-exchange-httpd_purses_get.c
index 799eeccb..dd904d79 100644
--- a/src/exchange/taler-exchange-httpd_purses_get.c
+++ b/src/exchange/taler-exchange-httpd_purses_get.c
@@ -15,7 +15,7 @@
 */
 /**
  * @file taler-exchange-httpd_purses_get.c
- * @brief Handle GET /purses/$PID requests
+ * @brief Handle GET /purses/$PID/$TARGET requests
  * @author Christian Grothoff
  */
 #include "platform.h"
@@ -23,67 +23,350 @@
 #include <jansson.h>
 #include <microhttpd.h>
 #include "taler_mhd_lib.h"
-#include "taler-exchange-httpd_purses.h"
+#include "taler_dbevents.h"
+#include "taler-exchange-httpd_keys.h"
+#include "taler-exchange-httpd_purses_get.h"
 #include "taler-exchange-httpd_mhd.h"
 #include "taler-exchange-httpd_responses.h"
 
-// FIXME: add long-polling support!
 
-MHD_RESULT
-TEH_handler_pursess_get (struct TEH_RequestContext *rc,
-                         const char *const args[1])
+/**
+ * Information about an ongoing /purses GET operation.
+ */
+struct GetContext
 {
+  /**
+   * Kept in a DLL.
+   */
+  struct GetContext *next;
+
+  /**
+   * Kept in a DLL.
+   */
+  struct GetContext *prev;
+
+  /**
+   * Connection we are handling.
+   */
+  struct MHD_Connection *connection;
+
+  /**
+   * Subscription for the database event we are
+   * waiting for.
+   */
+  struct GNUNET_DB_EventHandler *eh;
+
+  /**
+   * Public key of our purse.
+   */
   struct TALER_PurseContractPublicKeyP purse_pub;
-  enum GNUNET_DB_QueryStatus qs;
-  MHD_RESULT res;
-  struct GNUNET_TIME_Timestamp merge_timestamp = {0};
 
-  if (GNUNET_OK !=
-      GNUNET_STRINGS_string_to_data (args[0],
-                                     strlen (args[0]),
-                                     &purse_pub,
-                                     sizeof (purse_pub)))
+  /**
+   * When does this purse expire?
+   */
+  struct GNUNET_TIME_Timestamp purse_expiration;
+
+  /**
+   * When was this purse merged?
+   */
+  struct GNUNET_TIME_Timestamp merge_timestamp;
+
+  /**
+   * When was the full amount deposited into this purse?
+   */
+  struct GNUNET_TIME_Timestamp deposit_timestamp;
+
+  /**
+   * How much is the purse (supposed) to be worth?
+   */
+  struct TALER_Amount amount;
+
+  /**
+   * How much was deposited into the purse so far?
+   */
+  struct TALER_Amount deposited;
+
+  /**
+   * Hash over the contract of the purse.
+   */
+  struct TALER_PrivateContractHashP h_contract;
+
+  /**
+   * When will this request time out?
+   */
+  struct GNUNET_TIME_Absolute timeout;
+
+  /**
+   * true to wait for merge, false to wait for deposit.
+   */
+  bool wait_for_merge;
+
+  /**
+   * True if we are still suspended.
+   */
+  bool suspended;
+};
+
+
+/**
+ * Head of DLL of suspended GET requests.
+ */
+static struct GetContext *gc_head;
+
+/**
+ * Tail of DLL of suspended GET requests.
+ */
+static struct GetContext *gc_tail;
+
+
+void
+TEH_purses_get_cleanup ()
+{
+  struct GetContext *gc;
+
+  while (NULL != (gc = gc_head))
   {
-    GNUNET_break_op (0);
-    return TALER_MHD_reply_with_error (rc->connection,
-                                       MHD_HTTP_BAD_REQUEST,
-                                       
TALER_EC_EXCHANGE_PURSES_INVALID_PURSE_PUB,
-                                       args[0]);
+    GNUNET_CONTAINER_DLL_remove (gc_head,
+                                 gc_tail,
+                                 gc);
+    if (gc->suspended)
+    {
+      gc->suspended = false;
+      MHD_resume_connection (gc->connection);
+    }
+  }
+}
+
+
+/**
+ * Function called once a connection is done to
+ * clean up the `struct GetContext` state.
+ *
+ * @param rc context to clean up for
+ */
+static void
+gc_cleanup (struct TEH_RequestContext *rc)
+{
+  struct GetContext *gc = rc->rh_ctx;
+
+  GNUNET_assert (! gc->suspended);
+  if (NULL != gc->eh)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Cancelling DB event listening\n");
+    TEH_plugin->event_listen_cancel (TEH_plugin->cls,
+                                     gc->eh);
+    gc->eh = NULL;
   }
+  GNUNET_free (gc);
+}
+
+
+/**
+ * Function called on events received from Postgres.
+ * Wakes up long pollers.
+ *
+ * @param cls the `struct TEH_RequestContext *`
+ * @param extra additional event data provided
+ * @param extra_size number of bytes in @a extra
+ */
+static void
+db_event_cb (void *cls,
+             const void *extra,
+             size_t extra_size)
+{
+  struct TEH_RequestContext *rc = cls;
+  struct GetContext *gc = rc->rh_ctx;
+  struct GNUNET_AsyncScopeSave old_scope;
+
+  (void) extra;
+  (void) extra_size;
+  if (NULL == gc)
+    return; /* event triggered while main transaction
+               was still running */
+  if (! gc->suspended)
+    return; /* might get multiple wake-up events */
+  gc->suspended = false;
+  GNUNET_async_scope_enter (&rc->async_scope_id,
+                            &old_scope);
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Resuming from long-polling on purse\n");
+  TEH_check_invariants ();
+  GNUNET_CONTAINER_DLL_remove (gc_head,
+                               gc_tail,
+                               gc);
+  MHD_resume_connection (gc->connection);
+  TALER_MHD_daemon_trigger ();
+  TEH_check_invariants ();
+  GNUNET_async_scope_restore (&old_scope);
+}
+
+
+MHD_RESULT
+TEH_handler_purses_get (struct TEH_RequestContext *rc,
+                        const char *const args[2])
+{
+  struct GetContext *gc = rc->rh_ctx;
+  MHD_RESULT res;
+
+  if (NULL == gc)
+  {
+    gc = GNUNET_new (struct GetContext);
+    rc->rh_ctx = gc;
+    rc->rh_cleaner = &gc_cleanup;
+    gc->connection = rc->connection;
+    if (GNUNET_OK !=
+        GNUNET_STRINGS_string_to_data (args[0],
+                                       strlen (args[0]),
+                                       &gc->purse_pub,
+                                       sizeof (gc->purse_pub)))
+    {
+      GNUNET_break_op (0);
+      return TALER_MHD_reply_with_error (rc->connection,
+                                         MHD_HTTP_BAD_REQUEST,
+                                         
TALER_EC_EXCHANGE_GENERIC_PURSE_PUB_MALFORMED,
+                                         args[0]);
+    }
+    if (0 == strcmp (args[1],
+                     "merge"))
+      gc->wait_for_merge = true;
+    else if (0 == strcmp (args[1],
+                          "deposit"))
+      gc->wait_for_merge = false;
+    else
+    {
+      GNUNET_break_op (0);
+      return TALER_MHD_reply_with_error (rc->connection,
+                                         MHD_HTTP_BAD_REQUEST,
+                                         
TALER_EC_EXCHANGE_PURSES_INVALID_WAIT_TARGET,
+                                         args[1]);
+    }
+
+    {
+      const char *long_poll_timeout_ms;
+
+      long_poll_timeout_ms
+        = MHD_lookup_connection_value (rc->connection,
+                                       MHD_GET_ARGUMENT_KIND,
+                                       "timeout_ms");
+      if (NULL != long_poll_timeout_ms)
+      {
+        unsigned int timeout_ms;
+        char dummy;
+        struct GNUNET_TIME_Relative timeout;
+
+        if (1 != sscanf (long_poll_timeout_ms,
+                         "%u%c",
+                         &timeout_ms,
+                         &dummy))
+        {
+          GNUNET_break_op (0);
+          return TALER_MHD_reply_with_error (rc->connection,
+                                             MHD_HTTP_BAD_REQUEST,
+                                             
TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                                             "timeout_ms (must be non-negative 
number)");
+        }
+        timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
+                                                 timeout_ms);
+        gc->timeout = GNUNET_TIME_relative_to_absolute (timeout);
+      }
+    }
+
+    if ( (GNUNET_TIME_absolute_is_future (gc->timeout)) &&
+         (NULL == gc->eh) )
+    {
+      struct TALER_PurseEventP rep = {
+        .header.size = htons (sizeof (rep)),
+        .header.type = htons (
+          gc->wait_for_merge
+        ? TALER_DBEVENT_EXCHANGE_PURSE_MERGED
+        : TALER_DBEVENT_EXCHANGE_PURSE_DEPOSITED),
+        .purse_pub = gc->purse_pub
+      };
+
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Starting DB event listening\n");
+      gc->eh = TEH_plugin->event_listen (
+        TEH_plugin->cls,
+        GNUNET_TIME_absolute_get_remaining (gc->timeout),
+        &rep.header,
+        &db_event_cb,
+        rc);
+    }
+  } /* end first-time initialization */
+
 #if FIXME
-  qs = TEH_plugin->select_purse (TEH_plugin->cls,
-                                 &purse_pub,
-                                 &merge_timestamp,
-                                 ...);
-  switch (qs)
   {
-  case GNUNET_DB_STATUS_HARD_ERROR:
-    GNUNET_break (0);
-    return TALER_MHD_reply_with_error (rc->connection,
-                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                       TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                       "select_purses");
-  case GNUNET_DB_STATUS_SOFT_ERROR:
-    GNUNET_break (0);
-    return TALER_MHD_reply_with_error (rc->connection,
-                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                       TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                       "select_purses");
-  case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+    enum GNUNET_DB_QueryStatus qs;
+
+    qs = TEH_plugin->select_purse (TEH_plugin->cls,
+                                   &gc->purse_pub,
+                                   &gc->purse_expiration,
+                                   &gc->amount,
+                                   &gc->deposited,
+                                   &gc->h_contract,
+                                   &gc->merge_timestamp,
+                                   &gc->deposit_timestamp);
+    switch (qs)
+    {
+    case GNUNET_DB_STATUS_HARD_ERROR:
+      GNUNET_break (0);
+      return TALER_MHD_reply_with_error (rc->connection,
+                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                         TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                         "select_purse");
+    case GNUNET_DB_STATUS_SOFT_ERROR:
+      GNUNET_break (0);
+      return TALER_MHD_reply_with_error (rc->connection,
+                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                         TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                         "select_purse");
+    case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+      return TALER_MHD_reply_with_error (rc->connection,
+                                         MHD_HTTP_NOT_FOUND,
+                                         TALER_EC_EXCHANGE_PURSE_UNKNOWN,
+                                         NULL);
+    case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+      break; /* handled below */
+    }
+  }
+  if (GNUNET_TIME_absolute_is_past (gc->purse_expiration))
+  {
     return TALER_MHD_reply_with_error (rc->connection,
-                                       MHD_HTTP_NOT_FOUND,
-                                       TALER_EC_EXCHANGE_PURSESS_UNKNOWN,
-                                       NULL);
-  case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
-    break; /* handled below */
+                                       MHD_HTTP_GONE,
+                                       TALER_EC_EXCHANGE_PURSE_EXPIRED,
+                                       GNUNET_TIME_timestamp2s (
+                                         gc->purse_expiration));
   }
 #endif
+
+  // FIXME: compare amount to deposited amount;
+  // if below, set 'deposit_timestamp' to zero!
+
+  if (GNUNET_TIME_absolute_is_future (gc->timeout) &&
+      ( ((gc->wait_for_merge) &&
+         GNUNET_TIME_absolute_is_zero (gc->merge_timestamp.abs_time)) ||
+        ((! gc->wait_for_merge) &&
+         GNUNET_TIME_absolute_is_zero (gc->deposit_timestamp.abs_time)) ))
+  {
+    gc->suspended = true;
+    GNUNET_CONTAINER_DLL_insert (gc_head,
+                                 gc_tail,
+                                 gc);
+    MHD_suspend_connection (gc->connection);
+    return MHD_YES;
+  }
+
+  // FIXME: add exchange signature!?
+  // FIXME: return amount?
   res = TALER_MHD_REPLY_JSON_PACK (
     rc->connection,
     MHD_HTTP_OK,
     GNUNET_JSON_pack_timestamp ("merge_timestamp",
-                                &merge_timestamp));
-  GNUNET_free (epurses);
+                                gc->merge_timestamp),
+    GNUNET_JSON_pack_timestamp ("deposit_timestamp",
+                                gc->deposit_timestamp)
+    );
   return res;
 }
 
diff --git a/src/exchange/taler-exchange-httpd_purses_get.h 
b/src/exchange/taler-exchange-httpd_purses_get.h
new file mode 100644
index 00000000..648b01c9
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_purses_get.h
@@ -0,0 +1,51 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2022 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
+
+  You should have received a copy of the GNU Affero General Public License 
along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_purses_get.h
+ * @brief Handle /purses/$PURSE_PUB/$TARGET GET requests
+ * @author Christian Grothoff
+ */
+#ifndef TALER_EXCHANGE_HTTPD_PURSES_GET_H
+#define TALER_EXCHANGE_HTTPD_PURSES_GET_H
+
+#include <microhttpd.h>
+#include "taler-exchange-httpd.h"
+
+
+/**
+ * Shutdown purses-get subsystem.  Resumes all
+ * suspended long-polling clients and cleans up
+ * data structures.
+ */
+void
+TEH_purses_get_cleanup (void);
+
+
+/**
+ * Handle a GET "/purses/$PID/$TARGET" request.  Parses the
+ * given "purse_pub" in @a args (which should contain the
+ * EdDSA public key of a purse) and then respond with the
+ * status of the purse.
+ *
+ * @param rc request context
+ * @param args array of additional options (length: 2, the purse_pub and a 
target)
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_handler_purses_get (struct TEH_RequestContext *rc,
+                        const char *const args[2]);
+
+#endif
diff --git a/src/include/taler_exchange_service.h 
b/src/include/taler_exchange_service.h
index 3095ac2b..18bde0fe 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -4127,8 +4127,8 @@ struct TALER_EXCHANGE_PurseGetHandle;
  *
  * @param exchange exchange handle
  * @param purse_priv private key of the purse
- * @param merge_timeout how long to wait for a merge to happen
- * @param deposit_timeout how long to wait for a deposit to happen
+ * @param timeout how long to wait for a change to happen
+ * @param wait_for_merge true to wait for a merge event, otherwise wait for a 
deposit event
  * @param cb function to call with the exchange's result
  * @param cb_cls closure for @a cb
  * @return the request handle; NULL upon error
@@ -4137,8 +4137,8 @@ struct TALER_EXCHANGE_PurseGetHandle *
 TALER_EXCHANGE_purse_get (
   struct TALER_EXCHANGE_Handle *exchange,
   const struct TALER_PurseContractPrivateKeyP *purse_priv,
-  struct GNUNET_TIME_Relative merge_timeout,
-  struct GNUNET_TIME_Relative deposit_timeout,
+  struct GNUNET_TIME_Relative timeout,
+  bool wait_for_merge,
   TALER_EXCHANGE_PurseGetCallback cb,
   void *cb_cls);
 
diff --git a/src/include/taler_exchangedb_plugin.h 
b/src/include/taler_exchangedb_plugin.h
index abc7a7aa..1548bf3d 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -121,6 +121,24 @@ struct TALER_ReserveEventP
 };
 
 
+/**
+ * Signature of events signalling a purse changed its status.
+ */
+struct TALER_PurseEventP
+{
+  /**
+   * Of type #TALER_DBEVENT_EXCHANGE_PURSE_MERGED or
+   * #TALER_DBEVENT_EXCHANGE_PURSE_DEPOSITED.
+   */
+  struct GNUNET_DB_EventHeaderP header;
+
+  /**
+   * Public key of the purse the event is about.
+   */
+  struct TALER_PurseContractPublicKeyP purse_pub;
+};
+
+
 /**
  * Signature of events signalling a KYC process was completed.
  */
diff --git a/src/lib/exchange_api_purses_get.c 
b/src/lib/exchange_api_purses_get.c
index 263c6a1c..b3cb7e66 100644
--- a/src/lib/exchange_api_purses_get.c
+++ b/src/lib/exchange_api_purses_get.c
@@ -94,6 +94,7 @@ handle_purse_get_finished (void *cls,
     break;
   case MHD_HTTP_OK:
     {
+      // FIXME: check exchange signature!
       struct GNUNET_JSON_Specification spec[] = {
         GNUNET_JSON_spec_timestamp ("merge_timestamp",
                                     &dr.details.success.merge_timestamp),
@@ -163,15 +164,15 @@ struct TALER_EXCHANGE_PurseGetHandle *
 TALER_EXCHANGE_purse_get (
   struct TALER_EXCHANGE_Handle *exchange,
   const struct TALER_PurseContractPrivateKeyP *purse_priv,
-  struct GNUNET_TIME_Relative merge_timeout,
-  struct GNUNET_TIME_Relative deposit_timeout,
+  struct GNUNET_TIME_Relative timeout,
+  bool wait_for_merge,
   TALER_EXCHANGE_PurseGetCallback cb,
   void *cb_cls)
 {
   struct TALER_EXCHANGE_PurseGetHandle *pgh;
   CURL *eh;
   struct TALER_PurseContractPublicKeyP purse_pub;
-  char arg_str[sizeof (purse_pub) * 2 + 48];
+  char arg_str[sizeof (purse_pub) * 2 + 64];
 
   if (GNUNET_YES !=
       TEAH_handle_is_ready (exchange))
@@ -188,18 +189,33 @@ TALER_EXCHANGE_purse_get (
   {
     char cpub_str[sizeof (purse_pub) * 2];
     char *end;
+    char timeout_str[32];
 
     end = GNUNET_STRINGS_data_to_string (&purse_pub,
                                          sizeof (purse_pub),
                                          cpub_str,
                                          sizeof (cpub_str));
     *end = '\0';
-    GNUNET_snprintf (arg_str,
-                     sizeof (arg_str),
-                     "/purses/%s",
-                     cpub_str);
+    GNUNET_snprintf (timeout_str,
+                     sizeof (timeout_str),
+                     "%llu",
+                     (unsigned long long)
+                     (timeout.rel_value_us
+                      / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us));
+    if (GNUNET_TIME_relative_is_zero (timeout))
+      GNUNET_snprintf (arg_str,
+                       sizeof (arg_str),
+                       "/purses/%s/%s",
+                       cpub_str,
+                       wait_for_merge ? "merge" : "deposit");
+    else
+      GNUNET_snprintf (arg_str,
+                       sizeof (arg_str),
+                       "/purses/%s/%s?timeout_ms=%s",
+                       cpub_str,
+                       wait_for_merge ? "merge" : "deposit",
+                       timeout_str);
   }
-
   pgh->url = TEAH_path_to_url (exchange,
                                arg_str);
   if (NULL == pgh->url)

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