gnunet-svn
[Top][All Lists]
Advanced

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

[taler-merchant] 102/277: towards idempotency in POST /private/transfers


From: gnunet
Subject: [taler-merchant] 102/277: towards idempotency in POST /private/transfers
Date: Sun, 05 Jul 2020 20:50:15 +0200

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

grothoff pushed a commit to branch master
in repository merchant.

commit 7b3fd48e42192c59b8eb402ce3e91187deac7fae
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Sat May 9 13:40:01 2020 +0200

    towards idempotency in POST /private/transfers
---
 .../taler-merchant-httpd_private-post-transfers.c  | 277 ++++++++++--------
 src/backenddb/merchant-0001.sql                    |   2 +
 src/backenddb/plugin_merchantdb_postgres.c         | 325 ++++++++++++++++++---
 src/include/taler_merchantdb_plugin.h              |  96 +++++-
 4 files changed, 524 insertions(+), 176 deletions(-)

diff --git a/src/backend/taler-merchant-httpd_private-post-transfers.c 
b/src/backend/taler-merchant-httpd_private-post-transfers.c
index fd9752d..519e69f 100644
--- a/src/backend/taler-merchant-httpd_private-post-transfers.c
+++ b/src/backend/taler-merchant-httpd_private-post-transfers.c
@@ -299,11 +299,12 @@ check_transfer (void *cls,
     GNUNET_break_op (0);
     ptc->check_transfer_result = GNUNET_SYSERR;
     /* Build the `TrackTransferConflictDetails` */
+    ptc->response_code = MHD_HTTP_ACCEPTED;
     ptc->response
       = TALER_MHD_make_json_pack (
           "{s:I, s:s, s:s, s:o, s:o,"
           " s:I, s:o, s:o, s:o, s:o,"
-          " s:o, s:o, s:o }",
+          " s:o, s:o, s:o, s:o, s:o }",
           "code",
           (json_int_t) TALER_EC_POST_TRANSFERS_CONFLICTING_REPORTS,
           "hint",
@@ -330,6 +331,10 @@ check_transfer (void *cls,
           GNUNET_JSON_from_data_auto (&ttd->h_contract_terms),
           "amount_with_fee",
           TALER_JSON_from_amount (amount_with_fee),
+          "coin_value",
+          TALER_JSON_from_amount (&ttd->coin_value),
+          "coin_fee",
+          TALER_JSON_from_amount (&ttd->coin_fee),
           "deposit_fee",
           TALER_JSON_from_amount (deposit_fee));
     return;
@@ -339,16 +344,14 @@ check_transfer (void *cls,
 
 
 /**
- * Check that the given @a wire_fee is what the
- * @a exchange_pub should charge at the @a execution_time.
- * If the fee is correct (according to our database),
- * return #GNUNET_OK.  If we do not have the fee structure
- * in our DB, we just accept it and return #GNUNET_NO;
- * if we have proof that the fee is bogus, we respond with
- * the proof to the client and return #GNUNET_SYSERR.
+ * Check that the given @a wire_fee is what the @a exchange_pub should charge
+ * at the @a execution_time.  If the fee is correct (according to our
+ * database), return #GNUNET_OK.  If we do not have the fee structure in our
+ * DB, we just accept it and return #GNUNET_NO; if we have proof that the fee
+ * is bogus, we respond with the proof to the client and return
+ * #GNUNET_SYSERR.
  *
  * @param ptc context of the transfer to respond to
- * @param json response from the exchange
  * @param execution_time time of the wire transfer
  * @param wire_fee fee claimed by the exchange
  * @return #GNUNET_SYSERR if we returned hard proof of
@@ -356,7 +359,6 @@ check_transfer (void *cls,
  */
 static int
 check_wire_fee (struct PostTransfersContext *ptc,
-                const json_t *json,
                 struct GNUNET_TIME_Absolute execution_time,
                 const struct TALER_Amount *wire_fee)
 {
@@ -397,11 +399,14 @@ check_wire_fee (struct PostTransfersContext *ptc,
     return GNUNET_OK;   /* expected_fee >= wire_fee */
   }
   /* Wire fee check failed, export proof to client */
-  resume_transfer_with_response (
-    ptc,
-    MHD_HTTP_FAILED_DEPENDENCY,
+  /* FIXME: This is not actually the *full* proof, as we are
+     not including the exchange's bogus response with the
+     signature claiming a different wire fee.  Also, this
+     error is not described in the API docs! */
+  ptc->response_code = MHD_HTTP_ACCEPTED;
+  ptc->response =
     TALER_MHD_make_json_pack (
-      "{s:I, s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:O}",
+      "{s:I, s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o}",
       "code", (json_int_t) TALER_EC_POST_TRANSFERS_JSON_BAD_WIRE_FEE,
       "wire_fee", TALER_JSON_from_amount (wire_fee),
       "execution_time", GNUNET_JSON_from_time_abs (execution_time),
@@ -410,8 +415,7 @@ check_wire_fee (struct PostTransfersContext *ptc,
       "start_date", GNUNET_JSON_from_time_abs (start_date),
       "end_date", GNUNET_JSON_from_time_abs (end_date),
       "master_sig", GNUNET_JSON_from_data_auto (&master_sig),
-      "master_pub", GNUNET_JSON_from_data_auto (&ptc->master_pub),
-      "json", json));
+      "master_pub", GNUNET_JSON_from_data_auto (&ptc->master_pub));
   GNUNET_free (wire_method);
   return GNUNET_SYSERR;
 }
@@ -452,85 +456,6 @@ wire_transfer_cb (void *cls,
     return;
   }
 
-  if (GNUNET_SYSERR ==
-      check_wire_fee (ptc,
-                      hr->reply,
-                      td->execution_time,
-                      &td->wire_fee))
-    return;
-
-  /* Now we want to double-check that any (Taler coin) deposit
-   * which is accounted into _this_ wire transfer, does exist
-   * into _our_ database.  This is the rationale: if the
-   * exchange paid us for it, we must have received it _beforehands_!
-   *
-   * details_length is how many (Taler coin) deposits have been
-   * aggregated into _this_ wire transfer.
-   *///
-  for (unsigned int i = 0; i < td->details_length; i++)
-  {
-    const struct TALER_TrackTransferDetails *ttd = &td->details[i];
-
-    ptc->current_offset = i;
-    ptc->current_detail = ttd;
-    /* Set the coin as "never seen" before. */
-    ptc->check_transfer_result = GNUNET_NO;
-    TMH_db->preflight (TMH_db->cls);
-    qs = TMH_db->lookup_deposits_by_contract_and_coin (TMH_db->cls,
-                                                       instance_id,
-                                                       &ttd->h_contract_terms,
-                                                       &ttd->coin_pub,
-                                                       &check_transfer,
-                                                       ptc);
-    if (0 > qs)
-    {
-      /* single, read-only SQL statements should never cause
-         serialization problems */
-      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
-      /* Always report on hard error as well to enable diagnostics */
-      GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
-      resume_transfer_with_error (
-        ptc,
-        MHD_HTTP_INTERNAL_SERVER_ERROR,
-        TALER_EC_POST_TRANSFERS_DB_FETCH_DEPOSIT_ERROR,
-        "failed to obtain deposit data from local database");
-      return;
-    }
-    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
-    {
-      /* The exchange says we made this deposit, but WE do not
-         recall making it (corrupted / unreliable database?)!
-         Well, let's say thanks and accept the money! */
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  "Failed to find payment data in DB\n");
-      ptc->check_transfer_result = GNUNET_OK;
-    }
-    if (GNUNET_NO == ptc->check_transfer_result)
-    {
-      /* Internal error: how can we have called #check_transfer()
-         but still have no result? */
-      GNUNET_break (0);
-      resume_transfer_with_error (ptc,
-                                  MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                  
TALER_EC_POST_TRANSFERS_DB_INTERNAL_LOGIC_ERROR,
-                                  "internal logic error");
-      return;
-    }
-    if (GNUNET_SYSERR == ptc->check_transfer_result)
-    {
-      /* #check_transfer() failed, report conflict! */
-      GNUNET_break_op (0);
-      GNUNET_assert (NULL != ptc->response);
-      resume_transfer_with_response (ptc,
-                                     MHD_HTTP_FAILED_DEPENDENCY,
-                                     ptc->response);
-      ptc->response = NULL;
-      return;
-    }
-  }
-
-  /* Response is consistent with the /deposit we made,
-     remember it for future reference */
   for (unsigned int r = 0; r<MAX_RETRIES; r++)
   {
     TMH_db->preflight (TMH_db->cls);
@@ -673,6 +598,81 @@ process_transfer_with_exchange (void *cls,
 }
 
 
+/**
+ * Now we want to double-check that any (Taler coin) deposit which is
+ * accounted into _this_ wire transfer, does exist into _our_ database.  This
+ * is the rationale: if the exchange paid us for it, we must have received it
+ * _beforehands_!
+ *
+ * @param cls a `struct PostTransfersContext`
+ * @param current_offset at which offset in the exchange's reply are the @a ttd
+ * @param ttd details about an aggregated transfer (to check)
+ */
+static void
+verify_exchange_claim_cb (void *cls,
+                          unsigned int current_offset,
+                          const struct TALER_TrackTransferDetails *ttd)
+{
+  struct PostTransfersContext *ptc = cls;
+  enum GNUNET_DB_QueryStatus qs;
+
+  if (0 != ptc->response_code)
+    return; /* already encountered an error */
+  ptc->current_offset = current_offset;
+  ptc->current_detail = ttd;
+  /* Set the coin as "never seen" before. */
+  ptc->check_transfer_result = GNUNET_NO;
+  TMH_db->preflight (TMH_db->cls);
+  qs = TMH_db->lookup_deposits_by_contract_and_coin (
+    TMH_db->cls,
+    ptc->hc->instance->settings.id,
+    &ttd->h_contract_terms,
+    &ttd->coin_pub,
+    &check_transfer,
+    ptc);
+  if (0 > qs)
+  {
+    /* single, read-only SQL statements should never cause
+       serialization problems */
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
+    /* Always report on hard error as well to enable diagnostics */
+    GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
+    ptc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
+    ptc->response
+      = TALER_MHD_make_error (TALER_EC_POST_TRANSFERS_DB_FETCH_DEPOSIT_ERROR,
+                              "failed to obtain deposit data from local 
database");
+    return;
+  }
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+  {
+    /* The exchange says we made this deposit, but WE do not
+       recall making it (corrupted / unreliable database?)!
+       Well, let's say thanks and accept the money! */
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Failed to find payment data in DB\n");
+    ptc->check_transfer_result = GNUNET_OK;
+  }
+  if (GNUNET_NO == ptc->check_transfer_result)
+  {
+    /* Internal error: how can we have called #check_transfer()
+       but still have no result? */
+    GNUNET_break (0);
+    ptc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
+    ptc->response =
+      TALER_MHD_make_error (TALER_EC_POST_TRANSFERS_DB_INTERNAL_LOGIC_ERROR,
+                            "internal logic error");
+    return;
+  }
+  if (GNUNET_SYSERR == ptc->check_transfer_result)
+  {
+    /* #check_transfer() failed, report conflict! */
+    GNUNET_break_op (0);
+    GNUNET_assert (NULL != ptc->response);
+    return;
+  }
+}
+
+
 /**
  * Represents an entry in the table used to sum up
  * individual deposits for each h_contract_terms/order_id
@@ -710,7 +710,7 @@ struct Entry
  * @param deposit_fee the fee charged for @a deposit_value
  */
 static void
-transfer_details_cb (void *cls,
+transfer_summary_cb (void *cls,
                      const char *order_id,
                      const struct TALER_Amount *deposit_value,
                      const struct TALER_Amount *deposit_fee)
@@ -846,6 +846,7 @@ TMH_private_post_transfers (const struct TMH_RequestHandler 
*rh,
     hc->cc = &transfer_cleanup;
   }
 
+queue:
   if (0 != ptc->response_code)
   {
     MHD_RESULT ret;
@@ -910,43 +911,86 @@ TMH_private_post_transfers (const struct 
TMH_RequestHandler *rh,
 
   /* Check if transfer data is in database! */
   {
-    struct GNUNET_CONTAINER_MultiHashMap *map;
     struct GNUNET_TIME_Absolute execution_time;
     struct TALER_Amount total_amount;
     struct TALER_Amount wire_fee;
+    bool verified;
 
     TMH_db->preflight (TMH_db->cls);
-    map = GNUNET_CONTAINER_multihashmap_create (16,
-                                                GNUNET_NO);
-    qs = TMH_db->lookup_transfer_details (TMH_db->cls,
-                                          hc->instance->settings.id,
-                                          ptc->exchange_url,
-                                          ptc->payto_uri,
-                                          &ptc->wtid,
-                                          &total_amount,
-                                          &wire_fee,
-                                          execution_time,
-                                          &transfer_details_cb,
-                                          map);
+    qs = TMH_db->lookup_transfer (TMH_db->cls,
+                                  ptc->exchange_url,
+                                  &ptc->wtid,
+                                  &total_amount,
+                                  &wire_fee,
+                                  &execution_time,
+                                  &verified);
     if (0 > qs)
     {
       /* Simple select queries should not cause serialization issues */
       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
       /* Always report on hard error as well to enable diagnostics */
       GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
-      GNUNET_CONTAINER_multihashmap_iterate (map,
-                                             &hashmap_free,
-                                             NULL);
-      GNUNET_CONTAINER_multihashmap_destroy (map);
       return TALER_MHD_reply_with_error (connection,
                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
                                          
TALER_EC_POST_TRANSFERS_DB_LOOKUP_ERROR,
                                          "Failed to query database about 
transfer details");
     }
-    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs)
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+      goto fetch;
+    if (! verified)
+    {
+      if (GNUNET_SYSERR ==
+          check_wire_fee (ptc,
+                          execution_time,
+                          &wire_fee))
+      {
+        GNUNET_assert (0 != ptc->response_code);
+        goto queue;
+      }
+
+      qs = TMH_db->lookup_transfer_details (TMH_db->cls,
+                                            ptc->exchange_url,
+                                            &ptc->wtid,
+                                            &verify_exchange_claim_cb,
+                                            ptc);
+      if (0 != ptc->response_code)
+        goto queue;
+      verified = true;
+      qs = TMH_db->set_transfer_status_to_verified (TMH_db->cls,
+                                                    ptc->exchange_url,
+                                                    &ptc->wtid);
+      GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
+    }
+
+    /* Short version: we already verified, generate the summary response */
+    GNUNET_assert (verified);
     {
+      struct GNUNET_CONTAINER_MultiHashMap *map;
       json_t *deposit_sums;
 
+      map = GNUNET_CONTAINER_multihashmap_create (16,
+                                                  GNUNET_NO);
+      qs = TMH_db->lookup_transfer_summary (TMH_db->cls,
+                                            ptc->exchange_url,
+                                            &ptc->wtid,
+                                            &transfer_summary_cb,
+                                            map);
+      if (0 > qs)
+      {
+        /* Simple select queries should not cause serialization issues */
+        GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
+        /* Always report on hard error as well to enable diagnostics */
+        GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
+        GNUNET_CONTAINER_multihashmap_iterate (map,
+                                               &hashmap_free,
+                                               NULL);
+        GNUNET_CONTAINER_multihashmap_destroy (map);
+        return TALER_MHD_reply_with_error (connection,
+                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                           
TALER_EC_POST_TRANSFERS_DB_LOOKUP_ERROR,
+                                           "Failed to query database about 
transfer details");
+      }
+
       deposit_sums = json_array ();
       GNUNET_assert (NULL != deposit_sums);
       GNUNET_CONTAINER_multihashmap_iterate (map,
@@ -961,11 +1005,12 @@ TMH_private_post_transfers (const struct 
TMH_RequestHandler *rh,
         "wire_fee", TALER_JSON_from_amount (&wire_fee),
         "execution_time", GNUNET_JSON_from_time_abs (execution_time),
         "deposit_sums", deposit_sums);
-    }
-  }
+    } /* end of 'verified == true' */
+  } /* end of 'transfer data in database' */
 
   /* reply not in database, ensure the POST is in the database, and
      start work to obtain the reply from the exchange */
+fetch:
   qs = TMH_db->insert_transfer (TMH_db->cls,
                                 ptc->hc->instance->settings.id,
                                 ptc->exchange_url,
diff --git a/src/backenddb/merchant-0001.sql b/src/backenddb/merchant-0001.sql
index 754b47f..9eb60e5 100644
--- a/src/backenddb/merchant-0001.sql
+++ b/src/backenddb/merchant-0001.sql
@@ -370,6 +370,8 @@ CREATE TABLE IF NOT EXISTS merchant_transfer_signatures
      REFERENCES merchant_transfers (credit_serial) ON DELETE CASCADE
   ,signkey_serial BIGINT NOT NULL
      REFERENCES merchant_exchange_signing_keys (signkey_serial) ON DELETE 
CASCADE
+  ,wire_fee_val INT8 NOT NULL
+  ,wire_fee_frac INT4 NOT NULL
   ,execution_time INT8 NOT NULL
   ,exchange_sig BYTEA NOT NULL CHECK (LENGTH(exchange_sig)=64)
   );
diff --git a/src/backenddb/plugin_merchantdb_postgres.c 
b/src/backenddb/plugin_merchantdb_postgres.c
index c196790..8e402d3 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -2848,6 +2848,7 @@ RETRY:
   {
     struct GNUNET_PQ_QueryParam params[] = {
       GNUNET_PQ_query_param_uint64 (&credit_serial),
+      TALER_PQ_query_param_amount (&td->wire_fee),
       GNUNET_PQ_query_param_absolute_time (&td->execution_time),
       GNUNET_PQ_query_param_auto_from_type (&td->exchange_sig),
       GNUNET_PQ_query_param_auto_from_type (&td->exchange_pub),
@@ -3118,14 +3119,109 @@ postgres_lookup_deposits_by_contract_and_coin (
 
 
 /**
- * Closure for #lookup_transfer_details_cb().
+ * Lookup transfer status.
+ *
+ * @param cls closure
+ * @param exchange_url the exchange that made the transfer
+ * @param wtid wire transfer subject
+ * @param[out] total_amount amount that was transferred (in total, minus @a 
wire_fee)
+ * @param[out] wire_fee the wire fee the exchange charged
+ * @param[out] execution_time when the transfer was executed by the exchange
+ * @param[out] verified did we confirm the transfer was OK
+ * @return transaction status
  */
-struct LookupTransferDetailsContext
+static enum GNUNET_DB_QueryStatus
+postgres_lookup_transfer (
+  void *cls,
+  const char *exchange_url,
+  const struct TALER_WireTransferIdentifierRawP *wtid,
+  struct TALER_Amount *total_amount,
+  struct TALER_Amount *wire_fee,
+  struct GNUNET_TIME_Absolute *execution_time,
+  bool *verified)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (exchange_url),
+    GNUNET_PQ_query_param_auto_from_type (wtid),
+    GNUNET_PQ_query_param_end
+  };
+  uint8_t verified8;
+  struct TALER_Amount credit_amount;
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    TALER_PQ_RESULT_SPEC_AMOUNT ("credit_amount",
+                                 &credit_amount),
+    TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
+                                 wire_fee),
+    GNUNET_PQ_result_spec_absolute_time ("execution_time",
+                                         execution_time),
+    GNUNET_PQ_result_spec_auto_from_type ("verified",
+                                          &verified8),
+    GNUNET_PQ_result_spec_end
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  check_connection (pg);
+  qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                 "lookup_transfer",
+                                                 params,
+                                                 rs);
+  if (qs > 0)
+  {
+    *verified = (0 != verified8);
+    // FIXME: unclear if table stores 'total' including or excluding fee :-(.
+    // Check and update DOCS and taler_exchange_service.h header!
+    if (0 >
+        TALER_amount_add (total_amount,
+                          &credit_amount,
+                          wire_fee))
+    {
+      GNUNET_break (0);
+      return GNUNET_DB_STATUS_HARD_ERROR;
+    }
+  }
+  return qs;
+}
+
+
+/**
+ * Set transfer status to verified.
+ *
+ * @param cls closure
+ * @param exchange_url the exchange that made the transfer
+ * @param wtid wire transfer subject
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_set_transfer_status_to_verified (
+  void *cls,
+  const char *exchange_url,
+  const struct TALER_WireTransferIdentifierRawP *wtid)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (wtid),
+    GNUNET_PQ_query_param_string (exchange_url),
+    GNUNET_PQ_query_param_end
+  };
+
+  check_connection (pg);
+  return GNUNET_PQ_eval_prepared_non_select (
+    pg->conn,
+    "set_transfer_status_to_verified",
+    params);
+}
+
+
+/**
+ * Closure for #lookup_transfer_summary_cb().
+ */
+struct LookupTransferSummaryContext
 {
   /**
    * Function to call for each order that was aggregated.
    */
-  TALER_MERCHANTDB_TransferDetailsCallback cb;
+  TALER_MERCHANTDB_TransferSummaryCallback cb;
 
   /**
    * Closure for @e cb.
@@ -3148,16 +3244,16 @@ struct LookupTransferDetailsContext
  * Function to be called with the results of a SELECT statement
  * that has returned @a num_results results.
  *
- * @param cls of type `struct LookupTransferDetailsContext *`
+ * @param cls of type `struct LookupTransferSummaryContext *`
  * @param result the postgres result
  * @param num_result the number of results in @a result
  */
 static void
-lookup_transfer_details_cb (void *cls,
+lookup_transfer_summary_cb (void *cls,
                             PGresult *result,
                             unsigned int num_results)
 {
-  struct LookupTransferDetailsContext *ltdc = cls;
+  struct LookupTransferSummaryContext *ltdc = cls;
   struct PostgresClosure *pg = ltdc->pg;
 
   for (unsigned int i = 0; i<num_results; i++)
@@ -3194,17 +3290,135 @@ lookup_transfer_details_cb (void *cls,
 }
 
 
+/**
+ * Lookup transfer summary.
+ *
+ * @param cls closure
+ * @param exchange_url the exchange that made the transfer
+ * @param wtid wire transfer subject
+ * @param cb function to call with detailed transfer data
+ * @param cb_cls closure for @a cb
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_lookup_transfer_summary (
+  void *cls,
+  const char *exchange_url,
+  const struct TALER_WireTransferIdentifierRawP *wtid,
+  TALER_MERCHANTDB_TransferSummaryCallback cb,
+  void *cb_cls)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (exchange_url),
+    GNUNET_PQ_query_param_auto_from_type (wtid),
+    GNUNET_PQ_query_param_end
+  };
+  struct LookupTransferSummaryContext ltdc = {
+    .cb  = cb,
+    .cb_cls = cb_cls,
+    .pg = pg
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  check_connection (pg);
+  qs = GNUNET_PQ_eval_prepared_multi_select (
+    pg->conn,
+    "lookup_transfer_summary",
+    params,
+    &lookup_transfer_summary_cb,
+    &ltdc);
+  if (0 >= qs)
+    return qs;
+  return ltdc.qs;
+}
+
+
+/**
+ * Closure for #lookup_transfer_details_cb().
+ */
+struct LookupTransferDetailsContext
+{
+  /**
+   * Function to call for each order that was aggregated.
+   */
+  TALER_MERCHANTDB_TransferDetailsCallback cb;
+
+  /**
+   * Closure for @e cb.
+   */
+  void *cb_cls;
+
+  /**
+   * Plugin context.
+   */
+  struct PostgresClosure *pg;
+
+  /**
+   * Transaction result.
+   */
+  enum GNUNET_DB_QueryStatus qs;
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls of type `struct LookupTransferDetailsContext *`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+lookup_transfer_details_cb (void *cls,
+                            PGresult *result,
+                            unsigned int num_results)
+{
+  struct LookupTransferDetailsContext *ltdc = cls;
+  struct PostgresClosure *pg = ltdc->pg;
+
+  for (unsigned int i = 0; i<num_results; i++)
+  {
+    uint32_t current_offset;
+    struct TALER_TrackTransferDetails ttd;
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_uint32 ("offset_in_exchange_list",
+                                    &current_offset),
+      GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
+                                            &ttd.h_contract_terms),
+      GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
+                                            &ttd.coin_pub),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_value",
+                                   &ttd.coin_value),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_fee",
+                                   &ttd.coin_fee),
+      GNUNET_PQ_result_spec_end
+    };
+
+    if (GNUNET_OK !=
+        GNUNET_PQ_extract_result (result,
+                                  rs,
+                                  i))
+    {
+      GNUNET_break (0);
+      ltdc->qs = GNUNET_DB_STATUS_HARD_ERROR;
+      return;
+    }
+    ltdc->qs = i + 1;
+    ltdc->cb (ltdc->cb_cls,
+              (unsigned int) current_offset,
+              &ttd);
+    GNUNET_PQ_cleanup_result (rs);
+  }
+}
+
+
 /**
  * Lookup transfer details.
  *
  * @param cls closure
- * @param instance_id instance to lookup payments for
  * @param exchange_url the exchange that made the transfer
- * @param payto_uri account that received the transfer
  * @param wtid wire transfer subject
- * @param total_amount amount that was transferred (in total, minus @a 
wire_fee)
- * @param wire_fee the wire fee the exchange charged
- * @param execution_time when the transfer was executed by the exchange
  * @param cb function to call with detailed transfer data
  * @param cb_cls closure for @a cb
  * @return transaction status
@@ -3212,25 +3426,15 @@ lookup_transfer_details_cb (void *cls,
 static enum GNUNET_DB_QueryStatus
 postgres_lookup_transfer_details (
   void *cls,
-  const char *instance_id,
   const char *exchange_url,
-  const char *payto_uri,
   const struct TALER_WireTransferIdentifierRawP *wtid,
-  const struct TALER_Amount *total_amount,
-  const struct TALER_Amount *wire_fee,
-  struct GNUNET_TIME_Absolute execution_time,
   TALER_MERCHANTDB_TransferDetailsCallback cb,
   void *cb_cls)
 {
   struct PostgresClosure *pg = cls;
-  struct TALER_Amount transfer_amount;
   struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_string (instance_id),
     GNUNET_PQ_query_param_string (exchange_url),
-    GNUNET_PQ_query_param_string (payto_uri),
     GNUNET_PQ_query_param_auto_from_type (wtid),
-    TALER_PQ_query_param_amount (&transfer_amount),
-    GNUNET_PQ_query_param_absolute_time (&execution_time),
     GNUNET_PQ_query_param_end
   };
   struct LookupTransferDetailsContext ltdc = {
@@ -3240,14 +3444,6 @@ postgres_lookup_transfer_details (
   };
   enum GNUNET_DB_QueryStatus qs;
 
-  if (0 >
-      TALER_amount_subtract (&transfer_amount,
-                             total_amount,
-                             wire_fee))
-  {
-    GNUNET_break (0);
-    return GNUNET_DB_STATUS_HARD_ERROR;
-  }
   check_connection (pg);
   qs = GNUNET_PQ_eval_prepared_multi_select (
     pg->conn,
@@ -5687,14 +5883,16 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
                             "INSERT INTO merchant_transfer_signatures"
                             "(credit_serial"
                             ",signkey_serial"
+                            ",wire_fee_val"
+                            ",wire_fee_frac"
                             ",execution_time"
                             ",exchange_sig) "
-                            "SELECT $1, signkey_serial, $2, $3"
+                            "SELECT $1, signkey_serial, $2, $3, $4, $5"
                             " FROM merchant_exchange_signing_keys"
-                            " WHERE exchange_pub=$4"
+                            " WHERE exchange_pub=$6"
                             "  ORDER BY start_date DESC"
                             "  LIMIT 1",
-                            4),
+                            6),
     /* for postgres_insert_transfer_details() */
     GNUNET_PQ_make_prepare ("insert_transfer_to_coin_mapping",
                             "INSERT INTO merchant_transfer_to_coin"
@@ -5755,8 +5953,30 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
                             "        FROM merchant_instances"
                             "       WHERE merchant_id=$1)",
                             3),
-    /* for postgres_lookup_transfer_details() */
-    GNUNET_PQ_make_prepare ("lookup_transfer_details",
+    /* for postgres_lookup_transfer() */
+    GNUNET_PQ_make_prepare ("lookup_transfer",
+                            "SELECT"
+                            " credit_amount_val"
+                            ",credit_amount_frac"
+                            ",wire_fee_val"
+                            ",wire_fee_frac"
+                            ",execution_time"
+                            ",verified"
+                            " FROM merchant_transfers"
+                            "  JOIN merchant_transfer_signatures USING 
(credit_serial)"
+                            "  JOIN merchant_accounts USING (account_serial)"
+                            " WHERE wtid=$2"
+                            "   AND exchange_url=$1",
+                            2),
+    /* for postgres_set_transfer_status_to_verified() */
+    GNUNET_PQ_make_prepare ("set_transfer_status_to_verified",
+                            "UPDATE merchant_transfers SET"
+                            " verified=TRUE"
+                            " WHERE wtid=$1"
+                            "   AND exchange_url=$2",
+                            2),
+    /* for postgres_lookup_transfer_summary() */
+    GNUNET_PQ_make_prepare ("lookup_transfer_summary",
                             "SELECT"
                             " order_id"
                             ",exchange_deposit_value_val"
@@ -5764,22 +5984,29 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
                             ",exchange_deposit_fee_val"
                             ",exchange_deposit_fee_frac"
                             " FROM merchant_transfers"
-                            "  JOIN merchant_accounts USING (account_serial)"
                             "  JOIN merchant_transfer_to_coin USING 
(credit_serial)"
-                            "  JOIN merchant_transfer_signatures USING 
(credit_serial)"
                             "  JOIN merchant_deposits USING (deposit_serial)"
                             "  JOIN merchant_contract_terms USING 
(order_serial)"
-                            " WHERE wtid=$4"
-                            "   AND merchant_transfers.exchange_url=$2"
-                            "   AND credit_amount_val=$5"
-                            "   AND credit_amount_frac=$6"
-                            "   AND payto_uri=$3"
-                            "   AND execution_time=$7"
-                            "   AND merchant_contract_terms.merchant_serial="
-                            "     (SELECT merchant_serial"
-                            "        FROM merchant_instances"
-                            "       WHERE merchant_id=$1)",
-                            7),
+                            " WHERE wtid=$2"
+                            "   AND merchant_transfers.exchange_url=$1",
+                            2),
+    /* for postgres_lookup_transfer_details() */
+    GNUNET_PQ_make_prepare ("lookup_transfer_details",
+                            "SELECT"
+                            " merchant_contract_terms.h_contract_terms"
+                            
",merchant_transfer_to_coin.offset_in_exchange_list"
+                            ",merchant_deposits.coin_pub"
+                            ",exchange_deposit_value_val"
+                            ",exchange_deposit_value_frac"
+                            ",exchange_deposit_fee_val"
+                            ",exchange_deposit_fee_frac"
+                            " FROM merchant_transfer_to_coin"
+                            "  JOIN merchant_deposits USING (deposit_serial)"
+                            "  JOIN merchant_contract_terms USING 
(order_serial)"
+                            "  JOIN merchant_transfers USING (credit_serial)"
+                            " WHERE merchant_transfers.wtid=$2"
+                            "   AND merchant_transfers.exchange_url=$1",
+                            2),
     /* OLD API: */
 
 #if 0
@@ -6101,6 +6328,10 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
   plugin->lookup_wire_fee = &postgres_lookup_wire_fee;
   plugin->lookup_deposits_by_contract_and_coin =
     &postgres_lookup_deposits_by_contract_and_coin;
+  plugin->lookup_transfer = &postgres_lookup_transfer;
+  plugin->set_transfer_status_to_verified =
+    &postgres_set_transfer_status_to_verified;
+  plugin->lookup_transfer_summary = &postgres_lookup_transfer_summary;
   plugin->lookup_transfer_details = &postgres_lookup_transfer_details;
 
 /* OLD API: */
diff --git a/src/include/taler_merchantdb_plugin.h 
b/src/include/taler_merchantdb_plugin.h
index ec584bf..a9816a0 100644
--- a/src/include/taler_merchantdb_plugin.h
+++ b/src/include/taler_merchantdb_plugin.h
@@ -403,13 +403,28 @@ enum TALER_MERCHANTDB_RefundStatus
  * @param deposit_fee the fee charged for @a deposit_value
  */
 typedef void
-(*TALER_MERCHANTDB_TransferDetailsCallback)(
+(*TALER_MERCHANTDB_TransferSummaryCallback)(
   void *cls,
   const char *order_id,
   const struct TALER_Amount *deposit_value,
   const struct TALER_Amount *deposit_fee);
 
 
+/**
+ * Function called with detailed information about a wire transfer and
+ * the underlying deposits that are being aggregated.
+ *
+ * @param cls closure
+ * @param current_offset offset in the exchange reply we are at
+ * @param ttd details about the transfer at @a current_offset
+ */
+typedef void
+(*TALER_MERCHANTDB_TransferDetailsCallback)(
+  void *cls,
+  unsigned int current_offset,
+  const struct TALER_TrackTransferDetails *ttd);
+
+
 /* **************** OLD: ******************** */
 
 /**
@@ -1328,16 +1343,76 @@ struct TALER_MERCHANTDB_Plugin
 
 
   /**
-   * Lookup transfer details.
+   * Lookup transfer status.
    *
    * @param cls closure
    * @param instance_id instance to lookup payments for
-   * @param exchange_url
-   * @param payto_uri
-   * @param wtid
-   * @param total_amount
-   * @param wire_fee
-   * @param execution_time
+   * @param exchange_url the exchange that made the transfer
+   * @param payto_uri account that received the transfer
+   * @param wtid wire transfer subject
+   * @param[out] total_amount amount that was transferred (in total, minus @a 
wire_fee)
+   * @param[out] wire_fee the wire fee the exchange charged
+   * @param[out] execution_time when the transfer was executed by the exchange
+   * @param[out] verified did we confirm the transfer was OK
+   * @return transaction status
+   */
+  enum GNUNET_DB_QueryStatus
+  (*lookup_transfer)(
+    void *cls,
+    const char *exchange_url,
+    const struct TALER_WireTransferIdentifierRawP *wtid,
+    struct TALER_Amount *total_amount,
+    struct TALER_Amount *wire_fee,
+    struct GNUNET_TIME_Absolute *execution_time,
+    bool *verified);
+
+
+  /**
+   * Set transfer status to verified.
+   *
+   * @param cls closure
+   * @param instance_id instance to lookup payments for
+   * @param exchange_url the exchange that made the transfer
+   * @param payto_uri account that received the transfer
+   * @param wtid wire transfer subject
+   * @return transaction status
+   */
+  enum GNUNET_DB_QueryStatus
+  (*set_transfer_status_to_verified)(
+    void *cls,
+    const char *exchange_url,
+    const struct TALER_WireTransferIdentifierRawP *wtid);
+
+
+  /**
+   * Lookup transfer summary (used if we already verified the details).
+   *
+   * @param cls closure
+   * @param instance_id instance to lookup payments for
+   * @param exchange_url the exchange that made the transfer
+   * @param payto_uri account that received the transfer
+   * @param wtid wire transfer subject
+   * @param cb function to call with detailed transfer data
+   * @param cb_cls closure for @a cb
+   * @return transaction status
+   */
+  enum GNUNET_DB_QueryStatus
+  (*lookup_transfer_summary)(
+    void *cls,
+    const char *exchange_url,
+    const struct TALER_WireTransferIdentifierRawP *wtid,
+    TALER_MERCHANTDB_TransferSummaryCallback cb,
+    void *cb_cls);
+
+
+  /**
+   * Lookup transfer details. Used if we still need to verify the details.
+   *
+   * @param cls closure
+   * @param instance_id instance to lookup payments for
+   * @param exchange_url the exchange that made the transfer
+   * @param payto_uri account that received the transfer
+   * @param wtid wire transfer subject
    * @param cb function to call with detailed transfer data
    * @param cb_cls closure for @a cb
    * @return transaction status
@@ -1345,13 +1420,8 @@ struct TALER_MERCHANTDB_Plugin
   enum GNUNET_DB_QueryStatus
   (*lookup_transfer_details)(
     void *cls,
-    const char *instance_id,
     const char *exchange_url,
-    const char *payto_uri,
     const struct TALER_WireTransferIdentifierRawP *wtid,
-    const struct TALER_Amount *total_amount,
-    const struct TALER_Amount *wire_fee,
-    struct GNUNET_TIME_Absolute execution_time,
     TALER_MERCHANTDB_TransferDetailsCallback cb,
     void *cb_cls);
 

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