gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [taler-merchant] branch master updated (3f1cb92 -> e1ea7e3)


From: gnunet
Subject: [GNUnet-SVN] [taler-merchant] branch master updated (3f1cb92 -> e1ea7e3)
Date: Thu, 02 Nov 2017 14:06:03 +0100

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

grothoff pushed a change to branch master
in repository merchant.

    from 3f1cb92  tests and bugfixes for /tip-authorize
     new 78e7999  towards implementing /tip-pickup (incomplete)
     new e1ea7e3  work on /tip-pickup implemenation (WiP)

The 2 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:
 src/backend/taler-merchant-httpd_tip-authorize.c |   1 +
 src/backend/taler-merchant-httpd_tip-pickup.c    | 490 ++++++++++++++++++++++-
 src/backenddb/plugin_merchantdb_postgres.c       |  60 ++-
 src/backenddb/test_merchantdb.c                  |  25 +-
 src/include/taler_merchantdb_plugin.h            |  18 +
 5 files changed, 586 insertions(+), 8 deletions(-)

diff --git a/src/backend/taler-merchant-httpd_tip-authorize.c 
b/src/backend/taler-merchant-httpd_tip-authorize.c
index e46a4a3..bdfd48c 100644
--- a/src/backend/taler-merchant-httpd_tip-authorize.c
+++ b/src/backend/taler-merchant-httpd_tip-authorize.c
@@ -155,6 +155,7 @@ MH_handler_tip_authorize (struct TMH_RequestHandler *rh,
                           justification,
                           &amount,
                           &mi->tip_reserve,
+                         mi->tip_exchange,
                           &expiration,
                           &tip_id);
   if (TALER_EC_NONE != ec)
diff --git a/src/backend/taler-merchant-httpd_tip-pickup.c 
b/src/backend/taler-merchant-httpd_tip-pickup.c
index 982ec1f..c735405 100644
--- a/src/backend/taler-merchant-httpd_tip-pickup.c
+++ b/src/backend/taler-merchant-httpd_tip-pickup.c
@@ -18,8 +18,415 @@
  * @brief implementation of /tip-pickup handler
  * @author Christian Grothoff
  */
+#include "platform.h"
 #include <microhttpd.h>
+#include <jansson.h>
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
 #include "taler-merchant-httpd.h"
+#include "taler-merchant-httpd_mhd.h"
+#include "taler-merchant-httpd_parsing.h"
+#include "taler-merchant-httpd_exchanges.h"
+#include "taler-merchant-httpd_responses.h"
+#include "taler-merchant-httpd_tip-pickup.h"
+
+
+/**
+ * Details about a planchet that the customer wants to obtain
+ * a withdrawal authorization.  This is the information that
+ * will need to be sent to the exchange to obtain the blind
+ * signature required to turn a planchet into a coin.
+ */
+struct PlanchetDetail
+{
+  
+  /**
+   * The complete withdraw request that we are building to sign.
+   * Built incrementally during the processing of the request.
+   */
+  struct TALER_WithdrawRequestPS wr;
+
+  /**
+   * Blinded coin (see GNUNET_CRYPTO_rsa_blind()).  Note: is malloc()'ed!
+   */
+  char *coin_ev;
+
+  /**
+   * Number of bytes in @a coin_ev.
+   */
+  size_t coin_ev_size;
+
+};
+
+
+/**
+ * Information we keep for individual calls
+ * to requests that parse JSON, but keep no other state.
+ */
+struct PickupContext
+{
+
+  /**
+   * This field MUST be first.
+   * FIXME: Explain why!
+   */
+  struct TM_HandlerContext hc;
+
+  /**
+   * Placeholder for #TMH_PARSE_post_json() to keep its internal state.
+   */
+
+  void *json_parse_context;
+
+  /**
+   * URI of the exchange this tip uses.
+   */
+  char *exchange_uri;
+
+  /**
+   * Operation we run to find the exchange (and get its /keys).
+   */
+  struct TMH_EXCHANGES_FindOperation *fo;
+  
+  /**
+   * Array of planchets of length @e planchets_len.
+   */
+  struct PlanchetDetail *planchets;
+
+  /**
+   * Tip ID that was supplied by the client.
+   */
+  struct GNUNET_HashCode tip_id;
+
+  /**
+   * Unique identifier for the pickup operation, used to detect
+   * duplicate requests (retries).
+   */
+  struct GNUNET_HashCode pickup_id;
+
+  /**
+   * Total value of the coins we are withdrawing.
+   */
+  struct TALER_Amount total;
+  
+  /**
+   * Length of @e planchets.
+   */
+  unsigned int planchets_len;
+
+  /**
+   * Error code, #TALER_EC_NONE as long as all is fine.
+   */
+  enum TALER_ErrorCode ec;
+
+  /**
+   * HTTP status code to return in combination with @e ec 
+   * if @e ec is not #TALER_EC_NONE.
+   */
+  unsigned int response_code;
+
+  /**
+   * Human-readable error hint to return.
+   * if @e ec is not #TALER_EC_NONE.
+   */
+  const char *error_hint;
+  
+};
+
+
+/**
+ * Custom cleanup routine for a `struct PickupContext`.
+ *
+ * @param hc the `struct PickupContext` to clean up.
+ */
+static void
+pickup_cleanup (struct TM_HandlerContext *hc)
+{
+  struct PickupContext *pc = (struct PickupContext *) hc;
+
+  if (NULL != pc->planchets)
+  {
+    for (unsigned int i=0;i<pc->planchets_len;i++)
+      GNUNET_free_non_null (pc->planchets[i].coin_ev);
+    GNUNET_free (pc->planchets);
+    pc->planchets = NULL;
+  }
+  if (NULL != pc->fo)
+  {
+    TMH_EXCHANGES_find_exchange_cancel (pc->fo);
+    pc->fo = NULL;
+  }
+  TMH_PARSE_post_cleanup_callback (pc->json_parse_context);
+  GNUNET_free_non_null (pc->exchange_uri);
+  GNUNET_free (pc);
+}
+
+
+/**
+ * Prepare (and eventually execute) a pickup.  Computes
+ * the "pickup ID" (by hashing the planchets and denomination keys),
+ * resolves the denomination keys and calculates the total
+ * amount to be picked up.  Then runs the pick up execution logic.
+ *
+ * @param connection MHD connection for sending the response
+ * @param tip_id which tip are we picking up
+ * @param pc pickup context 
+ * @return #MHD_YES upon success, #MHD_NO if
+ *         the connection ought to be dropped
+ */
+static int
+run_pickup (struct MHD_Connection *connection,
+           struct PickupContext *pc)
+{
+  struct TALER_ReservePrivateKeyP reserve_priv;
+  struct TALER_ReservePublicKeyP reserve_pub;
+  enum TALER_ErrorCode ec;
+  json_t *sigs;
+
+  if (TALER_EC_NONE != pc->ec)
+  {
+    return TMH_RESPONSE_reply_rc (connection,
+                                 pc->response_code,
+                                 pc->ec,
+                                 pc->error_hint);
+  }
+  ec = db->pickup_tip (db->cls,
+                      &pc->total,
+                      &pc->tip_id,
+                      &pc->pickup_id,
+                      &reserve_priv);
+  if (TALER_EC_NONE != ec)
+  {
+    unsigned int response_code;
+    
+    switch (ec)
+    {
+    default:
+      response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
+      break;
+    }
+    /* FIXME: be more specific in choice of HTTP status code */
+    return TMH_RESPONSE_reply_rc (connection,
+                                 response_code,
+                                 ec,
+                                 "Database error approving tip");
+  }
+  sigs = json_array ();
+  GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv.eddsa_priv,
+                                     &reserve_pub.eddsa_pub);
+  for (unsigned int i=0;i<pc->planchets_len;i++)
+  {
+    struct PlanchetDetail *pd = &pc->planchets[i];
+    struct TALER_ReserveSignatureP reserve_sig;
+    
+    pd->wr.reserve_pub = reserve_pub;
+    GNUNET_assert (GNUNET_OK ==
+                  GNUNET_CRYPTO_eddsa_sign (&reserve_priv.eddsa_priv,
+                                            &pd->wr.purpose,
+                                            &reserve_sig.eddsa_signature));
+    json_array_append_new (sigs,
+                          GNUNET_JSON_from_data_auto (&reserve_sig));
+  }
+  return TMH_RESPONSE_reply_json_pack (connection,
+                                       MHD_HTTP_OK,
+                                       "{s:o, s:o}",
+                                       "reserve_pub", 
GNUNET_JSON_from_data_auto (&reserve_pub),
+                                      "reserve_sigs", sigs);
+}
+
+
+/**
+ * Function called with the result of a #TMH_EXCHANGES_find_exchange()
+ * operation.
+ *
+ * @param cls closure with the `struct PickupContext`
+ * @param eh handle to the exchange context
+ * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if 
not available
+ * @param exchange_trusted #GNUNET_YES if this exchange is trusted by config
+ */
+static void
+exchange_found_cb (void *cls,
+                  struct TALER_EXCHANGE_Handle *eh,
+                  const struct TALER_Amount *wire_fee,
+                  int exchange_trusted)
+{
+  struct PickupContext *pc = cls;
+  const struct TALER_EXCHANGE_Keys *keys;
+  struct GNUNET_HashContext *hc;
+  struct TALER_Amount total;
+  int ae;
+
+  if (NULL == eh)
+  {
+    pc->ec = TALER_EC_TIP_PICKUP_EXCHANGE_DOWN;
+    pc->error_hint = "failed to contact exchange, check URL";
+    pc->response_code = MHD_HTTP_FAILED_DEPENDENCY;
+    return;
+  }
+  keys = TALER_EXCHANGE_get_keys (eh);
+  if (NULL == keys)
+  {
+    pc->ec = TALER_EC_TIP_PICKUP_EXCHANGE_LACKED_KEYS;
+    pc->error_hint = "could not obtain denomination keys from exchange, check 
URL";
+    pc->response_code = MHD_HTTP_FAILED_DEPENDENCY;
+    return;
+  }
+
+  ae = GNUNET_NO;
+  memset (&total,
+         0,
+         sizeof (total));
+  hc = GNUNET_CRYPTO_hash_context_start ();
+  for (unsigned int i=0;i<pc->planchets_len;i++)
+  {
+    struct PlanchetDetail *pd = &pc->planchets[i];
+    const struct TALER_EXCHANGE_DenomPublicKey *dk;
+    struct TALER_Amount amount_with_fee;
+
+    dk = TALER_EXCHANGE_get_denomination_key_by_hash (keys,
+                                                     
&pd->wr.h_denomination_pub);
+    if (NULL == dk)
+    {
+      pc->ec = TALER_EC_TIP_PICKUP_EXCHANGE_LACKED_KEY;
+      pc->error_hint = "could not find matching denomination key";
+      pc->response_code = MHD_HTTP_NOT_FOUND;
+      GNUNET_CRYPTO_hash_context_abort (hc);
+      TMH_trigger_daemon ();
+      return;
+    }
+    GNUNET_CRYPTO_hash_context_read (hc,
+                                    &pd->wr.h_denomination_pub,
+                                    sizeof (struct GNUNET_HashCode));
+    GNUNET_CRYPTO_hash_context_read (hc,
+                                    pd->coin_ev,
+                                    pd->coin_ev_size);
+    if (GNUNET_OK !=
+       TALER_amount_add (&amount_with_fee,
+                         &dk->value,
+                         &dk->fee_withdraw))
+      ae = GNUNET_YES;
+    if (0 == i)
+    {
+      total = amount_with_fee;
+    }
+    else
+    {
+      if (GNUNET_OK !=
+         TALER_amount_add (&total,
+                           &total,
+                           &amount_with_fee))
+       ae = GNUNET_YES;
+    }
+    TALER_amount_hton (&pd->wr.withdraw_fee,
+                      &dk->fee_withdraw);
+    TALER_amount_hton (&pd->wr.amount_with_fee,
+                      &amount_with_fee);
+  }
+  GNUNET_CRYPTO_hash_context_finish (hc,
+                                    &pc->pickup_id);  
+  if (GNUNET_YES == ae)
+  {
+    pc->ec = TALER_EC_TIP_PICKUP_EXCHANGE_AMOUNT_OVERFLOW;
+    pc->error_hint = "error computing total value of the tip";
+    pc->response_code = MHD_HTTP_BAD_REQUEST;
+    TMH_trigger_daemon ();
+    return;      
+  }
+  pc->total = total;
+  TMH_trigger_daemon ();
+}
+
+
+/**
+ * Prepare (and eventually execute) a pickup. Finds the exchange
+ * handle we need for #run_pickup().
+ *
+ * @param connection MHD connection for sending the response
+ * @param tip_id which tip are we picking up
+ * @param pc pickup context 
+ * @return #MHD_YES upon success, #MHD_NO if
+ *         the connection ought to be dropped
+ */
+static int
+prepare_pickup (struct MHD_Connection *connection,
+               struct PickupContext *pc)
+{
+  enum TALER_ErrorCode ec;
+
+  ec = db->lookup_exchange_by_tip (db->cls,
+                                  &pc->tip_id,
+                                  &pc->exchange_uri);
+  if (TALER_EC_NONE != ec)
+  {
+    unsigned int response_code;
+
+    switch (ec)
+    {
+    case TALER_EC_TIP_PICKUP_TIP_ID_UNKNOWN:
+      response_code = MHD_HTTP_NOT_FOUND;
+      break;
+    default:
+      response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
+      break;
+    }
+    return TMH_RESPONSE_reply_rc (connection,
+                                 response_code,
+                                 ec,
+                                 "Could not determine exchange URI for the 
given tip id");
+    
+  }
+  pc->fo = TMH_EXCHANGES_find_exchange (pc->exchange_uri,
+                                       NULL,
+                                       &exchange_found_cb,
+                                       pc);
+  if (NULL == pc->fo)
+  {
+    return TMH_RESPONSE_reply_rc (connection,
+                                 MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                 TALER_EC_INTERNAL_INVARIANT_FAILURE,
+                                 "consult server logs");
+  }
+  return MHD_YES;
+}
+
+
+/**
+ * Parse the given @a planchet into the @a pd.
+ *
+ * @param connection connection to use for error reporting
+ * @param planchet planchet data in JSON format
+ * @param[out] pd where to store the binary data
+ * @return #GNUNET_OK upon success, #GNUNET_NO if a response
+ *    was generated, #GNUNET_SYSERR to drop the connection
+ */
+static int
+parse_planchet (struct MHD_Connection *connection,
+               const json_t *planchet,
+               struct PlanchetDetail *pd)
+{
+  int ret;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
+                                &pd->wr.h_denomination_pub),
+    GNUNET_JSON_spec_varsize ("coin_ev",
+                             (void **) &pd->coin_ev,
+                             &pd->coin_ev_size),
+    GNUNET_JSON_spec_end()
+  };
+  
+  ret = TMH_PARSE_json_data (connection,
+                            planchet,
+                            spec);
+  if (GNUNET_OK != ret)
+    return ret;
+  pd->wr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
+  pd->wr.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS));
+  GNUNET_CRYPTO_hash (pd->coin_ev,
+                     pd->coin_ev_size,
+                     &pd->wr.h_coin_envelope);
+  return ret;
+}
+
 
 /**
  * Manages a /tip-pickup call, checking that the tip is authorized,
@@ -39,6 +446,85 @@ MH_handler_tip_pickup (struct TMH_RequestHandler *rh,
                        const char *upload_data,
                        size_t *upload_data_size)
 {
-  GNUNET_break (0); /* #5099: not implemented */
-  return MHD_NO;
+  int res;
+  struct GNUNET_HashCode tip_id;
+  json_t *planchets;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_fixed_auto ("tip_id", &tip_id),
+    GNUNET_JSON_spec_json ("planchets", &planchets),
+    GNUNET_JSON_spec_end()
+  };
+  struct PickupContext *pc;
+  json_t *root;
+
+  if (NULL == *connection_cls)
+  {
+    pc = GNUNET_new (struct PickupContext);
+    pc->hc.cc = &pickup_cleanup;
+    *connection_cls = pc;
+  }
+  else
+  {
+    pc = *connection_cls;
+  }
+  if (NULL != pc->planchets)
+  {
+    /* This actually happens when we are called much later
+       after an exchange /keys' request to obtain the DKs
+       (and not for each request). */
+    return run_pickup (connection,
+                      pc);
+  }
+  res = TMH_PARSE_post_json (connection,
+                             &pc->json_parse_context,
+                             upload_data,
+                             upload_data_size,
+                             &root);
+  if (GNUNET_SYSERR == res)
+    return MHD_NO;
+  /* the POST's body has to be further fetched */
+  if ( (GNUNET_NO == res) ||
+       (NULL == root) )
+    return MHD_YES;
+
+  res = TMH_PARSE_json_data (connection,
+                             root,
+                             spec);
+  if (GNUNET_YES != res)
+  {
+    GNUNET_break_op (0);
+    json_decref (root);
+    return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+  }
+  pc->planchets_len = json_array_size (planchets);
+  if (pc->planchets_len > 1024)
+  {
+    GNUNET_JSON_parse_free (spec);
+    json_decref (root);
+    return TMH_RESPONSE_reply_rc (connection,
+                                 MHD_HTTP_BAD_REQUEST,
+                                 
TALER_EC_TIP_PICKUP_EXCHANGE_TOO_MANY_PLANCHETS,
+                                 "limit of 1024 planchets exceeded by 
request");
+  }
+  pc->planchets = GNUNET_new_array (pc->planchets_len,
+                                   struct PlanchetDetail);
+  for (unsigned int i=0;i<pc->planchets_len;i++)
+  {
+    if (GNUNET_OK !=
+       (res = parse_planchet (connection,
+                              json_array_get (planchets,
+                                              i),
+                              &pc->planchets[i])))
+    {
+      GNUNET_JSON_parse_free (spec);
+      json_decref (root);
+      return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+    }
+  }
+  pc->tip_id = tip_id;
+  res = prepare_pickup (connection,
+                       pc);
+  GNUNET_JSON_parse_free (spec);
+  json_decref (root);
+  return res;
 }
diff --git a/src/backenddb/plugin_merchantdb_postgres.c 
b/src/backenddb/plugin_merchantdb_postgres.c
index e9111ca..f15374c 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -202,6 +202,7 @@ postgres_initialize (void *cls)
     GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_tips ("
                             " reserve_priv BYTEA NOT NULL CHECK 
(LENGTH(reserve_priv)=32)"
                             ",tip_id BYTEA NOT NULL CHECK (LENGTH(tip_id)=64)"
+                           ",exchange_uri VARCHAR NOT NULL"
                             ",justification VARCHAR NOT NULL"
                             ",timestamp INT8 NOT NULL"
                             ",amount_val INT8 NOT NULL" /* overall tip amount 
*/
@@ -546,6 +547,7 @@ postgres_initialize (void *cls)
                             "INSERT INTO merchant_tips"
                             "(reserve_priv"
                             ",tip_id"
+                           ",exchange_uri"
                             ",justification"
                             ",timestamp"
                             ",amount_val"
@@ -555,8 +557,8 @@ postgres_initialize (void *cls)
                             ",left_frac"
                             ",left_curr"
                             ") VALUES "
-                            "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
-                            10),
+                            "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)",
+                            11),
     GNUNET_PQ_make_prepare ("lookup_reserve_by_tip_id",
                             "SELECT"
                             " reserve_priv"
@@ -575,6 +577,12 @@ postgres_initialize (void *cls)
                             " WHERE pickup_id=$1"
                             " AND tip_id=$2",
                             2),
+    GNUNET_PQ_make_prepare ("find_exchange_by_tip",
+                            "SELECT"
+                            " exchange_uri"
+                            " FROM merchant_tips"
+                            " WHERE tip_id=$1",
+                            1),
     GNUNET_PQ_make_prepare ("update_tip_balance",
                             "UPDATE merchant_tips SET"
                             " left_val=$2"
@@ -2753,6 +2761,7 @@ postgres_enable_tip_reserve (void *cls,
  * @param justification why was the tip approved
  * @param amount how high is the tip (with fees)
  * @param reserve_priv which reserve is debited
+ * @param exchange_uri which exchange manages the tip
  * @param[out] expiration set to when the tip expires
  * @param[out] tip_id set to the unique ID for the tip
  * @return taler error code
@@ -2768,6 +2777,7 @@ postgres_authorize_tip (void *cls,
                         const char *justification,
                         const struct TALER_Amount *amount,
                         const struct TALER_ReservePrivateKeyP *reserve_priv,
+                       const char *exchange_uri,
                         struct GNUNET_TIME_Absolute *expiration,
                         struct GNUNET_HashCode *tip_id)
 {
@@ -2853,6 +2863,7 @@ postgres_authorize_tip (void *cls,
     struct GNUNET_PQ_QueryParam params[] = {
       GNUNET_PQ_query_param_auto_from_type (reserve_priv),
       GNUNET_PQ_query_param_auto_from_type (tip_id),
+      GNUNET_PQ_query_param_string (exchange_uri),
       GNUNET_PQ_query_param_string (justification),
       GNUNET_PQ_query_param_absolute_time (&now),
       TALER_PQ_query_param_amount (amount), /* overall amount */
@@ -2882,6 +2893,46 @@ postgres_authorize_tip (void *cls,
 
 
 /**
+ * Find out which exchange was associated with @a tip_id
+ *
+ * @param cls closure, typically a connection to the d
+ * @param tip_id the unique ID for the tip
+ * @param[out] exchange_uri set to the URI of the exchange
+ * @return transaction status, usually
+ *      #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
+ *      #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a credit_uuid already known
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_lookup_exchange_by_tip (void *cls,
+                                const struct GNUNET_HashCode *tip_id,
+                                char **exchange_uri)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (tip_id),
+    GNUNET_PQ_query_param_end
+  };
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_string ("exchange_uri",
+                                 exchange_uri),
+    GNUNET_PQ_result_spec_end
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                "find_exchange_by_tip",
+                                                params,
+                                                rs);
+  if (0 >= qs)
+  {
+    *exchange_uri = NULL;
+    return qs;
+  }
+  return qs;
+}
+
+
+/**
  * Pickup a tip over @a amount using pickup id @a pickup_id.
  *
  * @param cls closure, typically a connection to the db
@@ -2941,8 +2992,8 @@ postgres_pickup_tip (void *cls,
     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
       return TALER_EC_TIP_PICKUP_TIP_ID_UNKNOWN;
     return (GNUNET_DB_STATUS_HARD_ERROR == qs)
-        ? TALER_EC_TIP_PICKUP_DB_ERROR_HARD
-        : TALER_EC_TIP_PICKUP_DB_ERROR_SOFT;
+      ? TALER_EC_TIP_PICKUP_DB_ERROR_HARD
+      : TALER_EC_TIP_PICKUP_DB_ERROR_SOFT;
   }
 
   /* Check if pickup_id already exists */
@@ -3133,6 +3184,7 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
   plugin->mark_proposal_paid = &postgres_mark_proposal_paid;
   plugin->enable_tip_reserve = &postgres_enable_tip_reserve;
   plugin->authorize_tip = &postgres_authorize_tip;
+  plugin->lookup_exchange_by_tip = &postgres_lookup_exchange_by_tip;
   plugin->pickup_tip = &postgres_pickup_tip;
   plugin->start = postgres_start;
   plugin->commit = postgres_commit;
diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c
index 05db7c2..78e72e3 100644
--- a/src/backenddb/test_merchantdb.c
+++ b/src/backenddb/test_merchantdb.c
@@ -528,20 +528,21 @@ test_tipping ()
   struct TALER_Amount total;
   struct TALER_Amount amount;
   struct TALER_Amount inc;
+  char *uri;
 
   RND_BLK (&tip_reserve_priv);
-  if (TALER_EC_TIP_AUTHORIZE_RESERVE_UNKNOWN !=
+  if (TALER_EC_TIP_AUTHORIZE_RESERVE_NOT_ENABLED !=
       plugin->authorize_tip (plugin->cls,
                              "testing tips reserve unknown",
                              &amount,
                              &tip_reserve_priv,
+                            "http://localhost:8081/";,
                              &tip_expiration,
                              &tip_id))
   {
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
-
   RND_BLK (&tip_credit_uuid);
   GNUNET_assert (GNUNET_OK ==
                  TALER_string_to_amount (CURRENCY ":5",
@@ -578,6 +579,7 @@ test_tipping ()
                              "testing tips too late",
                              &amount,
                              &tip_reserve_priv,
+                            "http://localhost:8081/";,
                              &tip_expiration,
                              &tip_id))
   {
@@ -623,6 +625,7 @@ test_tipping ()
                              "testing tips",
                              &amount,
                              &tip_reserve_priv,
+                            "http://localhost:8081/";,
                              &tip_expiration,
                              &tip_id))
   {
@@ -634,11 +637,28 @@ test_tipping ()
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+      plugin->lookup_exchange_by_tip (plugin->cls,
+                                     &tip_id,
+                                     &uri))
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  if (0 != strcmp ("http://localhost:8081/";,
+                  uri))
+  {
+    GNUNET_free (uri);
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  GNUNET_free (uri);
   if (TALER_EC_NONE !=
       plugin->authorize_tip (plugin->cls,
                              "testing tips more",
                              &amount,
                              &tip_reserve_priv,
+                            "http://localhost:8081/";,
                              &tip_expiration,
                              &tip_id))
   {
@@ -711,6 +731,7 @@ test_tipping ()
                              "testing tips insufficient funds",
                              &amount,
                              &tip_reserve_priv,
+                            "http://localhost:8081/";,
                              &tip_expiration,
                              &tip_id))
   {
diff --git a/src/include/taler_merchantdb_plugin.h 
b/src/include/taler_merchantdb_plugin.h
index c5a323d..3e35374 100644
--- a/src/include/taler_merchantdb_plugin.h
+++ b/src/include/taler_merchantdb_plugin.h
@@ -656,6 +656,7 @@ struct TALER_MERCHANTDB_Plugin
    * @param justification why was the tip approved
    * @param amount how high is the tip (with fees)
    * @param reserve_priv which reserve is debited
+   * @param exchange_uri which exchange manages the tip
    * @param[out] expiration set to when the tip expires
    * @param[out] tip_id set to the unique ID for the tip
    * @return transaction status,
@@ -671,11 +672,28 @@ struct TALER_MERCHANTDB_Plugin
                    const char *justification,
                    const struct TALER_Amount *amount,
                    const struct TALER_ReservePrivateKeyP *reserve_priv,
+                  const char *exchange_uri,
                    struct GNUNET_TIME_Absolute *expiration,
                    struct GNUNET_HashCode *tip_id);
 
 
   /**
+   * Find out which exchange was associated with @a tip_id
+   *
+   * @param cls closure, typically a connection to the d
+   * @param tip_id the unique ID for the tip
+   * @param[out] exchange_uri set to the URI of the exchange
+   * @return transaction status, usually
+   *      #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
+   *      #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a credit_uuid already known
+   */
+  enum GNUNET_DB_QueryStatus
+  (*lookup_exchange_by_tip)(void *cls,
+                           const struct GNUNET_HashCode *tip_id,
+                           char **exchange_uri);
+  
+
+  /**
    * Pickup a tip over @a amount using pickup id @a pickup_id.
    *
    * @param cls closure, typically a connection to the db

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



reply via email to

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