gnunet-svn
[Top][All Lists]
Advanced

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

[taler-anastasis] branch master updated: implement #6750


From: gnunet
Subject: [taler-anastasis] branch master updated: implement #6750
Date: Sat, 10 Apr 2021 16:27:43 +0200

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

grothoff pushed a commit to branch master
in repository anastasis.

The following commit(s) were added to refs/heads/master by this push:
     new cdc01c1  implement #6750
cdc01c1 is described below

commit cdc01c1f6d38a6b45786a9c0b2ec1df097b88219
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Sat Apr 10 16:27:40 2021 +0200

    implement #6750
---
 src/backend/anastasis-httpd_truth.c     | 169 +++++++++++++++++++++++++++++++-
 src/include/anastasis_database_plugin.h |  15 +++
 src/stasis/plugin_anastasis_postgres.c  |  49 ++++++++-
 src/stasis/stasis-0001.sql              |   5 +-
 4 files changed, 233 insertions(+), 5 deletions(-)

diff --git a/src/backend/anastasis-httpd_truth.c 
b/src/backend/anastasis-httpd_truth.c
index 42aa452..6a94ce8 100644
--- a/src/backend/anastasis-httpd_truth.c
+++ b/src/backend/anastasis-httpd_truth.c
@@ -44,6 +44,13 @@
 #define CHECK_PAYMENT_GENERIC_TIMEOUT GNUNET_TIME_relative_multiply ( \
     GNUNET_TIME_UNIT_MINUTES, 30)
 
+/**
+ * How long should the wallet check for auto-refunds before giving up?
+ */
+#define AUTO_REFUND_TIMEOUT GNUNET_TIME_relative_multiply ( \
+    GNUNET_TIME_UNIT_MINUTES, 2)
+
+
 /**
  * How many retries do we allow per code?
  */
@@ -152,10 +159,55 @@ struct GetContext
    */
   bool have_response;
 
+};
+
+/**
+ * Information we track for refunds.
+ */
+struct RefundEntry
+{
+  /**
+   * Kept in a DLL.
+   */
+  struct RefundEntry *next;
+
+  /**
+   * Kept in a DLL.
+   */
+  struct RefundEntry *prev;
+
+  /**
+   * Operation handle.
+   */
+  struct TALER_MERCHANT_OrderRefundHandle *ro;
+
+  /**
+   * Which order is being refunded.
+   */
+  char *order_id;
+
+  /**
+   * Payment Identifier
+   */
+  struct ANASTASIS_PaymentSecretP payment_identifier;
 
+  /**
+   * Public key of the challenge which is solved.
+   */
+  struct ANASTASIS_CRYPTO_TruthUUIDP truth_uuid;
 };
 
 
+/**
+ * Head of linked list of active refund operations.
+ */
+static struct RefundEntry *re_head;
+
+/**
+ * Tail of linked list of active refund operations.
+ */
+static struct RefundEntry *re_tail;
+
 /**
  * Head of linked list over all authorization processes
  */
@@ -171,6 +223,20 @@ void
 AH_truth_shutdown (void)
 {
   struct GetContext *gc;
+  struct RefundEntry *re;
+
+  while (NULL != (re = re_head))
+  {
+    GNUNET_CONTAINER_DLL_remove (re_head,
+                                 re_tail,
+                                 re);
+    TALER_MERCHANT_post_order_refund_cancel (re->ro);
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Refund `%s' failed due to shutdown\n",
+                re->order_id);
+    GNUNET_free (re->order_id);
+    GNUNET_free (re);
+  }
 
   while (NULL != (gc = gc_head))
   {
@@ -204,6 +270,99 @@ AH_truth_shutdown (void)
 }
 
 
+/**
+ * Callback to process a POST /orders/ID/refund request
+ *
+ * @param cls closure
+ * @param http_status HTTP status code for this request
+ * @param ec taler-specific error code
+ * @param taler_refund_uri the refund uri offered to the wallet
+ * @param h_contract hash of the contract a Browser may need to authorize
+ *        obtaining the HTTP response.
+ */
+static void
+refund_cb (
+  void *cls,
+  const struct TALER_MERCHANT_HttpResponse *hr,
+  const char *taler_refund_uri,
+  const struct GNUNET_HashCode *h_contract)
+{
+  struct RefundEntry *re = cls;
+
+  re->ro = NULL;
+  switch (hr->http_status)
+  {
+  case MHD_HTTP_OK:
+    {
+      enum GNUNET_DB_QueryStatus qs;
+
+      qs = db->record_challenge_refund (db->cls,
+                                        &re->truth_uuid,
+                                        &re->payment_identifier);
+      switch (qs)
+      {
+      case GNUNET_DB_STATUS_HARD_ERROR:
+        GNUNET_break (0);
+        break;
+      case GNUNET_DB_STATUS_SOFT_ERROR:
+        GNUNET_break (0);
+        break;
+      case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+        GNUNET_break (0);
+        break;
+      case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+        break;
+      }
+    }
+    break;
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Refund `%s' failed with HTTP status %u: %s (#%u)\n",
+                re->order_id,
+                hr->http_status,
+                hr->hint,
+                (unsigned int) hr->ec);
+  }
+  GNUNET_CONTAINER_DLL_remove (re_head,
+                               re_tail,
+                               re);
+  GNUNET_free (re->order_id);
+  GNUNET_free (re);
+}
+
+
+/**
+ * Start to give a refund for the challenge created by @a gc.
+ *
+ * @param gc request where we failed and should now grant a refund for
+ */
+static void
+begin_refund (const struct GetContext *gc)
+{
+  struct RefundEntry *re;
+
+  re = GNUNET_new (struct RefundEntry);
+  re->order_id = GNUNET_STRINGS_data_to_string_alloc (
+    &gc->payment_identifier,
+    sizeof (gc->payment_identifier));
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Challenge execution failed, triggering refund for order `%s'\n",
+              re->order_id);
+  re->payment_identifier = gc->payment_identifier;
+  re->truth_uuid = gc->truth_uuid;
+  re->ro = TALER_MERCHANT_post_order_refund (AH_ctx,
+                                             AH_backend_url,
+                                             re->order_id,
+                                             &gc->challenge_cost,
+                                             "failed to issue challenge",
+                                             &refund_cb,
+                                             re);
+  GNUNET_CONTAINER_DLL_insert (re_head,
+                               re_tail,
+                               re);
+}
+
+
 /**
  * Callback used to notify the application about completed requests.
  * Cleans up the requests data structures.
@@ -553,10 +712,12 @@ begin_payment (struct GetContext *gc)
     pay_deadline = GNUNET_TIME_relative_to_absolute (
       ANASTASIS_CHALLENGE_OFFER_LIFETIME);
     GNUNET_TIME_round_abs (&pay_deadline);
-    order = json_pack ("{s:o, s:s, s:s, s:o}",
+    order = json_pack ("{s:o, s:s, s:s, s:o, s:o}",
                        "amount", TALER_JSON_from_amount (&gc->challenge_cost),
                        "summary", "challenge fee for anastasis service",
                        "order_id", order_id,
+                       "auto_refund", GNUNET_JSON_from_time_rel (
+                         AUTO_REFUND_TIMEOUT),
                        "pay_deadline", GNUNET_JSON_from_time_abs (
                          pay_deadline));
     gc->po = TALER_MERCHANT_orders_post2 (AH_ctx,
@@ -668,8 +829,10 @@ run_authorization_process (struct MHD_Connection 
*connection,
     gc->as = NULL;
     return MHD_YES;
   case ANASTASIS_AUTHORIZATION_RES_FAILED:
-    /* Challenge transmission failed, our fault! */
-    // FIXME #6750: give at least a refund!?
+    if (gc->payment_identifier_provided)
+    {
+      begin_refund (gc);
+    }
     gc->authorization->cleanup (gc->as);
     gc->as = NULL;
     return MHD_YES;
diff --git a/src/include/anastasis_database_plugin.h 
b/src/include/anastasis_database_plugin.h
index d51ac6b..4853375 100644
--- a/src/include/anastasis_database_plugin.h
+++ b/src/include/anastasis_database_plugin.h
@@ -591,6 +591,21 @@ struct ANASTASIS_DatabasePlugin
     const struct TALER_Amount *amount);
 
 
+  /**
+   * Record refund for challenge.
+   *
+   * @param cls closure
+   * @param truth_key identifier of the challenge to pay
+   * @param payment_secret payment secret which the user must provide with 
every upload
+   * @return transaction status
+   */
+  enum GNUNET_DB_QueryStatus
+  (*record_challenge_refund)(
+    void *cls,
+    const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
+    const struct ANASTASIS_PaymentSecretP *payment_secret);
+
+
   /**
    * Lookup for a pending payment for a certain challenge
    *
diff --git a/src/stasis/plugin_anastasis_postgres.c 
b/src/stasis/plugin_anastasis_postgres.c
index 977d2f1..6750e47 100644
--- a/src/stasis/plugin_anastasis_postgres.c
+++ b/src/stasis/plugin_anastasis_postgres.c
@@ -945,6 +945,34 @@ postgres_record_challenge_payment (
 }
 
 
+/**
+ * Store refund granted for challenge.
+ *
+ * @param cls closure
+ * @param truth_key identifier of the challenge to pay
+ * @param payment_secret payment secret which the user must provide with every 
upload
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_record_challenge_refund (
+  void *cls,
+  const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
+  const struct ANASTASIS_PaymentSecretP *payment_secret)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (payment_secret),
+    GNUNET_PQ_query_param_auto_from_type (truth_uuid),
+    GNUNET_PQ_query_param_end
+  };
+
+  check_connection (pg);
+  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                             "challenge_refund_update",
+                                             params);
+}
+
+
 /**
  * Check payment identifier. Used to check if a payment identifier given by
  * the user is valid (existing and paid).
@@ -1842,6 +1870,17 @@ libanastasis_plugin_db_postgres_init (void *cls)
                             " AND"
                             "  paid=FALSE;",
                             2),
+    GNUNET_PQ_make_prepare ("challenge_refund_update",
+                            "UPDATE anastasis_challenge_payment "
+                            "SET"
+                            " refunded=TRUE "
+                            "WHERE"
+                            "  payment_identifier=$1"
+                            " AND"
+                            "  paid=TRUE"
+                            " AND"
+                            "  truth_uuid=$2;",
+                            2),
     GNUNET_PQ_make_prepare ("challenge_payment_done",
                             "UPDATE anastasis_challenge_payment "
                             "SET"
@@ -1849,6 +1888,8 @@ libanastasis_plugin_db_postgres_init (void *cls)
                             "WHERE"
                             "  payment_identifier=$1"
                             " AND"
+                            "  refunded=FALSE"
+                            " AND"
                             "  truth_uuid=$2"
                             " AND"
                             "  paid=FALSE;",
@@ -1879,6 +1920,7 @@ libanastasis_plugin_db_postgres_init (void *cls)
                             " FROM anastasis_challenge_payment"
                             " WHERE payment_identifier=$1"
                             "   AND truth_uuid=$2"
+                            "   AND refunded=FALSE"
                             "   AND counter>0;",
                             1),
     GNUNET_PQ_make_prepare ("challenge_pending_payment_select",
@@ -1891,6 +1933,8 @@ libanastasis_plugin_db_postgres_init (void *cls)
                             " WHERE"
                             "  paid=FALSE"
                             " AND"
+                            "  refunded=FALSE"
+                            " AND"
                             "  truth_uuid=$1"
                             " AND"
                             "  creation_date > $2;",
@@ -1931,7 +1975,9 @@ libanastasis_plugin_db_postgres_init (void *cls)
     GNUNET_PQ_make_prepare ("gc_challenge_pending_payments",
                             "DELETE FROM anastasis_challenge_payment "
                             "WHERE"
-                            "  paid=FALSE"
+                            "  (paid=FALSE"
+                            "   OR"
+                            "   refunded=TRUE)"
                             " AND"
                             "  creation_date < $1;",
                             1),
@@ -2133,6 +2179,7 @@ libanastasis_plugin_db_postgres_init (void *cls)
   plugin->record_truth_upload_payment = &postgres_record_truth_upload_payment;
   plugin->check_truth_upload_paid = &postgres_check_truth_upload_paid;
   plugin->record_challenge_payment = &postgres_record_challenge_payment;
+  plugin->record_challenge_refund = &postgres_record_challenge_refund;
   plugin->check_challenge_payment = &postgres_check_challenge_payment;
   plugin->lookup_challenge_payment = &postgres_lookup_challenge_payment;
   plugin->update_challenge_payment = &postgres_update_challenge_payment;
diff --git a/src/stasis/stasis-0001.sql b/src/stasis/stasis-0001.sql
index 7025029..beb886d 100644
--- a/src/stasis/stasis-0001.sql
+++ b/src/stasis/stasis-0001.sql
@@ -109,7 +109,8 @@ CREATE TABLE IF NOT EXISTS anastasis_challenge_payment
    payment_identifier BYTEA NOT NULL CHECK(LENGTH(payment_identifier)=32),
    creation_date INT8 NOT NULL,
    counter INT4 NOT NULL DEFAULT 3,
-   paid BOOLEAN NOT NULL DEFAULT FALSE
+   paid BOOLEAN NOT NULL DEFAULT FALSE,
+   refunded BOOLEAN NOT NULL DEFAULT FALSE
   );
 COMMENT ON TABLE anastasis_recdoc_payment
   IS 'Records a payment for a challenge';
@@ -129,6 +130,8 @@ COMMENT ON COLUMN anastasis_challenge_payment.creation_date
   IS 'Creation date of the payment';
 COMMENT ON COLUMN anastasis_challenge_payment.paid
   IS 'Is the payment finished';
+COMMENT ON COLUMN anastasis_challenge_payment.refunded
+  IS 'Was the payment refunded';
 
 
 CREATE TABLE IF NOT EXISTS anastasis_recoverydocument

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