gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [taler-exchange] branch master updated: work on #5077: rese


From: gnunet
Subject: [GNUnet-SVN] [taler-exchange] branch master updated: work on #5077: reserve_pub vs. wtid issue, add reject functionality to wire plugin API (with stub implementations for now)
Date: Sun, 12 Nov 2017 15:46:55 +0100

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 8440de1  work on #5077: reserve_pub vs. wtid issue, add reject 
functionality to wire plugin API (with stub implementations for now)
8440de1 is described below

commit 8440de13339a3b38d9efa18b69df409e45cde625
Author: Christian Grothoff <address@hidden>
AuthorDate: Sun Nov 12 15:46:52 2017 +0100

    work on #5077: reserve_pub vs. wtid issue, add reject functionality to wire 
plugin API (with stub implementations for now)
---
 src/auditor/taler-wire-auditor.c              |  80 +++++++---
 src/exchange/taler-exchange-wirewatch.c       | 110 +++++++++++++-
 src/include/taler_error_codes.h               |   4 +
 src/include/taler_wire_plugin.h               |  71 ++++++++-
 src/wire/plugin_wire_sepa.c                   | 103 +++++++++++++
 src/wire/plugin_wire_test.c                   | 202 ++++++++++++++++++++------
 src/wire/test_wire_plugin_transactions_test.c |   5 +-
 7 files changed, 503 insertions(+), 72 deletions(-)

diff --git a/src/auditor/taler-wire-auditor.c b/src/auditor/taler-wire-auditor.c
index 4ee9256..9c73990 100644
--- a/src/auditor/taler-wire-auditor.c
+++ b/src/auditor/taler-wire-auditor.c
@@ -70,7 +70,8 @@ static struct GNUNET_CONTAINER_MultiHashMap *in_map;
 
 /**
  * Map with information about outgoing wire transfers.
- * Maps hashes of the wire offsets to `struct ReserveOutInfo`s.
+ * Maps hashes of the wire subjects (in binary encoding)
+ * to `struct ReserveOutInfo`s.
  */
 static struct GNUNET_CONTAINER_MultiHashMap *out_map;
 
@@ -658,7 +659,9 @@ complain_out_not_found (void *cls,
                      "row", (json_int_t) 0,
                      "amount_wired", TALER_JSON_from_amount 
(&roi->details.amount),
                      "amount_justified", TALER_JSON_from_amount (&zero),
-                     "wtid", GNUNET_JSON_from_data_auto 
(&roi->details.reserve_pub), /* #5077 missnomer */
+                     "wtid", (NULL == roi->details.wtid_s)
+                     ? GNUNET_JSON_from_data_auto (&roi->details.wtid)
+                     : json_string (roi->details.wtid_s),
                      "timestamp", GNUNET_STRINGS_absolute_time_to_string 
(roi->details.execution_date),
                      "diagnostic", "justification for wire transfer not 
found"));
   GNUNET_break (GNUNET_OK ==
@@ -726,6 +729,7 @@ history_debit_cb (void *cls,
                  const struct TALER_WIRE_TransferDetails *details)
 {
   struct ReserveOutInfo *roi;
+  struct GNUNET_HashCode rowh;
 
   if (TALER_BANK_DIRECTION_NONE == dir)
   {
@@ -735,13 +739,36 @@ history_debit_cb (void *cls,
     check_exchange_wire_out ();
     return GNUNET_OK;
   }
+  if (NULL != details->wtid_s)
+  {
+    char *diagnostic;
+
+    GNUNET_CRYPTO_hash (row_off,
+                        row_off_size,
+                        &rowh);
+    GNUNET_asprintf (&diagnostic,
+                     "malformed wire transfer subject `%s'",
+                     details->wtid_s);
+    report (report_row_inconsistencies,
+            json_pack ("{s:s, s:I, s:o, s:o, s:s}",
+                       "table", "bank wire log",
+                       "row", (json_int_t) 0,
+                       "amount", TALER_JSON_from_amount (&details->amount),
+                       "wire_offset_hash", GNUNET_JSON_from_data_auto (&rowh),
+                       "diagnostic", diagnostic));
+    /* TODO: report generator currently ignores 'amount' for this
+       table, maybe use a different table to report this issue! */
+    /* TODO: add 'amount' to some total amount that was badly wired! */
+    GNUNET_free (diagnostic);
+    return GNUNET_SYSERR;
+  }
   roi = GNUNET_new (struct ReserveOutInfo);
-  GNUNET_CRYPTO_hash (&details->reserve_pub, /* FIXME (#5077): missnomer */
-                     sizeof (details->reserve_pub),
+  GNUNET_CRYPTO_hash (&details->wtid,
+                     sizeof (details->wtid),
                      &roi->subject_hash);
   roi->details.amount = details->amount;
   roi->details.execution_date = details->execution_date;
-  roi->details.reserve_pub = details->reserve_pub; /* FIXME (#5077): missnomer 
& redundant */
+  roi->details.wtid = details->wtid;
   roi->details.account_details = json_incref ((json_t *) 
details->account_details);
   if (GNUNET_OK !=
       GNUNET_CONTAINER_multihashmap_put (out_map,
@@ -749,13 +776,25 @@ history_debit_cb (void *cls,
                                         roi,
                                         
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
   {
-    GNUNET_break_op (0); /* duplicate wire offset is not allowed! */
+    char *diagnostic;
+
+    GNUNET_CRYPTO_hash (row_off,
+                        row_off_size,
+                        &rowh);
+    GNUNET_asprintf (&diagnostic,
+                     "duplicate wire transfer subject `%s'",
+                     TALER_B2S (&roi->subject_hash));
     report (report_row_inconsistencies,
-            json_pack ("{s:s, s:I, s:o, s:s}",
+            json_pack ("{s:s, s:I, s:o, s:o, s:s}",
                        "table", "bank wire log",
                        "row", (json_int_t) 0,
-                       "wire_offset_hash", GNUNET_JSON_from_data_auto 
(&roi->subject_hash),
-                       "diagnostic", "duplicate wire offset"));
+                       "amount", TALER_JSON_from_amount (&details->amount),
+                       "wire_offset_hash", GNUNET_JSON_from_data_auto (&rowh),
+                       "diagnostic", diagnostic));
+    /* TODO: report generator currently ignores 'amount' for this
+       table, maybe use a different table to report this issue! */
+    /* TODO: add 'amount' to some total amount that was badly wired! */
+    GNUNET_free (diagnostic);
     return GNUNET_SYSERR;
   }
   return GNUNET_OK;
@@ -830,7 +869,12 @@ reserve_in_cb (void *cls,
   rii->row_off_size = wire_reference_size;
   rii->details.amount = *credit;
   rii->details.execution_date = execution_date;
-  rii->details.reserve_pub = *reserve_pub;
+  /* reserve public key should be the WTID */
+  GNUNET_assert (sizeof (rii->details.wtid) ==
+                 sizeof (*reserve_pub));
+  memcpy (&rii->details.wtid,
+          reserve_pub,
+          sizeof (*reserve_pub));
   rii->details.account_details = json_incref ((json_t *) 
sender_account_details);
   rii->rowid = rowid;
   if (GNUNET_OK !=
@@ -875,7 +919,7 @@ complain_in_not_found (void *cls,
                      "row", (json_int_t) rii->rowid,
                      "amount_expected", TALER_JSON_from_amount 
(&rii->details.amount),
                      "amount_wired", TALER_JSON_from_amount (&zero),
-                     "wtid", GNUNET_JSON_from_data_auto 
(&rii->details.reserve_pub), /* also reserve_pub, but see #5077 missnomer */
+                     "wtid", GNUNET_JSON_from_data_auto (&rii->details.wtid),
                      "timestamp", GNUNET_STRINGS_absolute_time_to_string 
(rii->details.execution_date),
                      "diagnostic", "incoming wire transfer claimed by exchange 
not found"));
   GNUNET_break (GNUNET_OK ==
@@ -966,16 +1010,16 @@ history_credit_cb (void *cls,
                        "diagnostic", "wire reference size missmatch"));
     return GNUNET_OK;
   }
-  if (0 != memcmp (&details->reserve_pub,
-                  &rii->details.reserve_pub,
-                  sizeof (struct TALER_ReservePublicKeyP)))
+  if (0 != memcmp (&details->wtid,
+                  &rii->details.wtid,
+                  sizeof (struct TALER_WireTransferIdentifierRawP)))
   {
     report (report_reserve_in_inconsistencies,
             json_pack ("{s:I, s:o, s:o, s:o, s:s, s:s}",
                        "row", GNUNET_JSON_from_data (row_off, row_off_size),
                        "amount_exchange_expected", TALER_JSON_from_amount 
(&rii->details.amount),
                        "amount_wired", TALER_JSON_from_amount (&zero),
-                       "wtid", GNUNET_JSON_from_data_auto 
(&rii->details.reserve_pub), /* #5077 missnomer */
+                       "wtid", GNUNET_JSON_from_data_auto (&rii->details.wtid),
                        "timestamp", GNUNET_STRINGS_absolute_time_to_string 
(rii->details.execution_date),
                        "diagnostic", "wire subject does not match"));
     GNUNET_break (GNUNET_OK ==
@@ -987,7 +1031,7 @@ history_credit_cb (void *cls,
                        "row", GNUNET_JSON_from_data (row_off, row_off_size),
                        "amount_exchange_expected", TALER_JSON_from_amount 
(&zero),
                        "amount_wired", TALER_JSON_from_amount 
(&details->amount),
-                       "wtid", GNUNET_JSON_from_data_auto 
(&details->reserve_pub), /* #5077 missnomer */
+                       "wtid", GNUNET_JSON_from_data_auto (&details->wtid),
                        "timestamp", GNUNET_STRINGS_absolute_time_to_string 
(details->execution_date),
                        "diagnostic", "wire subject does not match"));
 
@@ -1005,7 +1049,7 @@ history_credit_cb (void *cls,
                        "row", GNUNET_JSON_from_data (row_off, row_off_size),
                        "amount_exchange_expected", TALER_JSON_from_amount 
(&rii->details.amount),
                        "amount_wired", TALER_JSON_from_amount 
(&details->amount),
-                       "wtid", GNUNET_JSON_from_data_auto 
(&details->reserve_pub), /* #5077 missnomer */
+                       "wtid", GNUNET_JSON_from_data_auto (&details->wtid),
                        "timestamp", GNUNET_STRINGS_absolute_time_to_string 
(details->execution_date),
                        "diagnostic", "wire amount does not match"));
     if (0 < TALER_amount_cmp (&details->amount,
@@ -1046,7 +1090,7 @@ history_credit_cb (void *cls,
             json_pack ("{s:s, s:o, s:o}",
                        "amount", TALER_JSON_from_amount (&rii->details.amount),
                        "row", GNUNET_JSON_from_data (row_off, row_off_size),
-                       "wtid", GNUNET_JSON_from_data_auto 
(&rii->details.reserve_pub))); /* FIXME #5077 missnomer */
+                       "wtid", GNUNET_JSON_from_data_auto 
(&rii->details.wtid)));
     GNUNET_break (GNUNET_OK ==
                   TALER_amount_add (&total_missattribution_in,
                                     &total_missattribution_in,
diff --git a/src/exchange/taler-exchange-wirewatch.c 
b/src/exchange/taler-exchange-wirewatch.c
index 312f8ac..ca7f3ba 100644
--- a/src/exchange/taler-exchange-wirewatch.c
+++ b/src/exchange/taler-exchange-wirewatch.c
@@ -37,6 +37,23 @@
 
 
 /**
+ * Closure for #reject_cb().
+ */
+struct RejectContext
+{
+  /**
+   * Wire transfer subject that was illformed.
+   */
+  char *wtid_s;
+
+  /**
+   * Database session that encountered the problem.
+   */
+  struct TALER_EXCHANGEDB_Session *session;
+};
+
+
+/**
  * Handle to the plugin.
  */
 static struct TALER_WIRE_Plugin *wire_plugin;
@@ -109,6 +126,11 @@ static struct GNUNET_SCHEDULER_Task *task;
  */
 static struct TALER_WIRE_HistoryHandle *hh;
 
+/**
+ * Active request to reject a wire transfer.
+ */
+static struct TALER_WIRE_RejectHandle *rt;
+
 
 /**
  * We're being aborted with CTRL-C (or SIGTERM). Shut down.
@@ -129,6 +151,15 @@ shutdown_task (void *cls)
                                     hh);
     hh = NULL;
   }
+  if (NULL != rt)
+  {
+    char *wtid_s;
+
+    wtid_s = wire_plugin->reject_transfer_cancel (wire_plugin->cls,
+                                                  rt);
+    rt = NULL;
+    GNUNET_free (wtid_s);
+  }
   TALER_EXCHANGEDB_plugin_unload (db_plugin);
   db_plugin = NULL;
   TALER_WIRE_plugin_unload (wire_plugin);
@@ -205,6 +236,48 @@ find_transfers (void *cls);
 
 
 /**
+ * Function called upon completion of the rejection of a wire transfer.
+ *
+ * @param cls closure with the `struct RejectContext`
+ * @param ec error code for the operation
+ */
+static void
+reject_cb (void *cls,
+           enum TALER_ErrorCode ec)
+{
+  struct RejectContext *rtc = cls;
+  enum GNUNET_DB_QueryStatus qs;
+
+  rt = NULL;
+  if (TALER_EC_NONE != ec)
+  {
+    fprintf (stderr,
+             "Failed to wire back transfer `%s': %d\n",
+             rtc->wtid_s,
+             ec);
+    GNUNET_free (rtc->wtid_s);
+    db_plugin->rollback (db_plugin->cls,
+                        rtc->session);
+    GNUNET_free (rtc);
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  GNUNET_free (rtc->wtid_s);
+  qs = db_plugin->commit (db_plugin->cls,
+                          rtc->session);
+  GNUNET_free (rtc);
+  if (0 <= qs)
+  {
+    GNUNET_free_non_null (start_off);
+    start_off = last_row_off;
+    start_off_size = last_row_off_size;
+  }
+  task = GNUNET_SCHEDULER_add_now (&find_transfers,
+                                   NULL);
+}
+
+
+/**
  * Callbacks of this type are used to serve the result of asking
  * the bank for the transaction history.
  *
@@ -224,6 +297,7 @@ history_cb (void *cls,
 {
   struct TALER_EXCHANGEDB_Session *session = cls;
   enum GNUNET_DB_QueryStatus qs;
+  struct TALER_ReservePublicKeyP reserve_pub;
 
   if (TALER_BANK_DIRECTION_NONE == dir)
   {
@@ -254,13 +328,45 @@ history_cb (void *cls,
                                       NULL);
     return GNUNET_OK; /* will be ignored anyway */
   }
+  if (NULL != details->wtid_s)
+  {
+    struct RejectContext *rtc;
+
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Wire transfer over %s has invalid subject `%s', sending it 
back!\n",
+                TALER_amount2s (&details->amount),
+                details->wtid_s);
+    if (last_row_off_size != row_off_size)
+    {
+      GNUNET_free_non_null (last_row_off);
+      last_row_off = GNUNET_malloc (row_off_size);
+    }
+    memcpy (last_row_off,
+            row_off,
+            row_off_size);
+    rtc = GNUNET_new (struct RejectContext);
+    rtc->session = session;
+    rtc->wtid_s = GNUNET_strdup (details->wtid_s);
+    rt = wire_plugin->reject_transfer (wire_plugin->cls,
+                                       row_off,
+                                       row_off_size,
+                                       &reject_cb,
+                                       rtc);
+    return GNUNET_SYSERR; /* will continue later... */
+  }
+
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Adding wire transfer over %s with subject `%s'\n",
               TALER_amount2s (&details->amount),
-              TALER_B2S (&details->reserve_pub));
+              TALER_B2S (&details->wtid));
+  /* Wire transfer identifier == reserve public key */
+  GNUNET_assert (sizeof (reserve_pub) == sizeof (details->wtid));
+  memcpy (&reserve_pub,
+          &details->wtid,
+          sizeof (reserve_pub));
   qs = db_plugin->reserves_in_insert (db_plugin->cls,
                                      session,
-                                     &details->reserve_pub,
+                                     &reserve_pub,
                                      &details->amount,
                                      details->execution_date,
                                      details->account_details,
diff --git a/src/include/taler_error_codes.h b/src/include/taler_error_codes.h
index 65831f4..931b5ee 100644
--- a/src/include/taler_error_codes.h
+++ b/src/include/taler_error_codes.h
@@ -63,6 +63,10 @@ enum TALER_ErrorCode
    */
   TALER_EC_INTERNAL_INVARIANT_FAILURE = 5,
 
+  /**
+   * Operation timed out.
+   */
+  TALER_EC_TIMEOUT = 6,
 
   /* ********** generic error codes ************* */
 
diff --git a/src/include/taler_wire_plugin.h b/src/include/taler_wire_plugin.h
index 969af35..6e355ba 100644
--- a/src/include/taler_wire_plugin.h
+++ b/src/include/taler_wire_plugin.h
@@ -59,13 +59,17 @@ struct TALER_WIRE_TransferDetails
   struct GNUNET_TIME_Absolute execution_date;
 
   /**
-   * Reserve public key that was encoded in the wire transfer subject.
-   * FIXME (#5077): this is incorrect for *outgoing* wire transfers.
-   * Maybe use `struct TALER_WireTransferIdentifierRawP` here instead?
-   * OTOH, we might want to make this even more generic in case of
-   * invalid transfers, so that we can capture those as well!
+   * Binary data that was encoded in the wire transfer subject, if
+   * it decoded properly.  Otherwise all-zeros and @e wtid_s is set.
    */
-  struct TALER_ReservePublicKeyP reserve_pub;
+  struct TALER_WireTransferIdentifierRawP wtid;
+
+  /**
+   * Wire transfer identifer as a string.  Set to NULL if the
+   * identifier was properly Base32 encoded and this @e wtid could be
+   * set instead.
+   */
+  char *wtid_s;
 
   /**
    * The other account that was involved
@@ -94,6 +98,18 @@ typedef int
 
 
 /**
+ * Callbacks of this type are used to serve the result of asking
+ * the bank to reject a wire transfer.
+ *
+ * @param cls closure
+ * @param ec status of the operation, #TALER_EC_NONE on success
+ */
+typedef void
+(*TALER_WIRE_RejectTransferCallback) (void *cls,
+                                      enum TALER_ErrorCode ec);
+
+
+/**
  * Handle returned for cancelling a preparation step.
  */
 struct TALER_WIRE_PrepareHandle;
@@ -308,12 +324,55 @@ struct TALER_WIRE_Plugin
   /**
    * Cancel going over the account's history.
    *
+   * @param cls plugins' closure
    * @param whh operation to cancel
    */
   void
   (*get_history_cancel) (void *cls,
                         struct TALER_WIRE_HistoryHandle *whh);
 
+
+  /**
+   * Reject an incoming wire transfer that was obtained from the
+   * history. This function can be used to transfer funds back to
+   * the sender if the WTID was malformed (i.e. due to a typo).
+   *
+   * Calling `reject_transfer` twice on the same wire transfer should
+   * be idempotent, i.e. not cause the funds to be wired back twice.
+   * Furthermore, the transfer should henceforth be removed from the
+   * results returned by @e get_history.
+   *
+   * @param cls plugin's closure
+   * @param start_off offset of the wire transfer in plugin-specific format
+   * @param start_off_len number of bytes in @a start_off
+   * @param rej_cb function to call with the result of the operation
+   * @param rej_cb_cls closure for @a rej_cb
+   * @return handle to cancel the operation
+   */
+  struct TALER_WIRE_RejectHandle *
+  (*reject_transfer)(void *cls,
+                     const void *start_off,
+                     size_t start_off_len,
+                     TALER_WIRE_RejectTransferCallback rej_cb,
+                     void *rej_cb_cls);
+
+  /**
+   * Cancel ongoing reject operation.  Note that the rejection may still
+   * proceed. Basically, if this function is called, the rejection may
+   * have happened or not.  This function is usually used during shutdown
+   * or system upgrades.  At a later point, the application must call
+   * @e reject_transfer again for this wire transfer, unless the
+   * @e get_history shows that the wire transfer no longer exists.
+   *
+   * @param cls plugins' closure
+   * @param rh operation to cancel
+   * @return closure of the callback of the operation
+   */
+  void *
+  (*reject_transfer_cancel)(void *cls,
+                            struct TALER_WIRE_RejectHandle *rh);
+
+
 };
 
 
diff --git a/src/wire/plugin_wire_sepa.c b/src/wire/plugin_wire_sepa.c
index 5de3472..416acac 100644
--- a/src/wire/plugin_wire_sepa.c
+++ b/src/wire/plugin_wire_sepa.c
@@ -791,6 +791,107 @@ sepa_get_history_cancel (void *cls,
 }
 
 
+
+/**
+ * Context for a rejection operation.
+ */
+struct TALER_WIRE_RejectHandle
+{
+  /**
+   * Function to call with the result.
+   */
+  TALER_WIRE_RejectTransferCallback rej_cb;
+
+  /**
+   * Closure for @e rej_cb.
+   */
+  void *rej_cb_cls;
+
+  /**
+   * Handle to task for timeout of operation.
+   */
+  struct GNUNET_SCHEDULER_Task *timeout_task;
+};
+
+
+/**
+ * Rejection operation failed with timeout, notify callback
+ * and clean up.
+ *
+ * @param cls closure with `struct TALER_WIRE_RejectHandle`
+ */
+static void
+timeout_reject (void *cls)
+{
+  struct TALER_WIRE_RejectHandle *rh = cls;
+
+  rh->timeout_task = NULL;
+  rh->rej_cb (rh->rej_cb_cls,
+              TALER_EC_NOT_IMPLEMENTED /* in the future: TALER_EC_TIMEOUT */);
+  GNUNET_free (rh);
+}
+
+
+/**
+ * Reject an incoming wire transfer that was obtained from the
+ * history. This function can be used to transfer funds back to
+ * the sender if the WTID was malformed (i.e. due to a typo).
+ *
+ * Calling `reject_transfer` twice on the same wire transfer should
+ * be idempotent, i.e. not cause the funds to be wired back twice.
+ * Furthermore, the transfer should henceforth be removed from the
+ * results returned by @e get_history.
+ *
+ * @param cls plugin's closure
+ * @param start_off offset of the wire transfer in plugin-specific format
+ * @param start_off_len number of bytes in @a start_off
+ * @param rej_cb function to call with the result of the operation
+ * @param rej_cb_cls closure for @a rej_cb
+ * @return handle to cancel the operation
+ */
+static struct TALER_WIRE_RejectHandle *
+sepa_reject_transfer (void *cls,
+                      const void *start_off,
+                      size_t start_off_len,
+                      TALER_WIRE_RejectTransferCallback rej_cb,
+                      void *rej_cb_cls)
+{
+  struct TALER_WIRE_RejectHandle *rh;
+
+  GNUNET_break (0); /* not implemented, just a stub! */
+  rh = GNUNET_new (struct TALER_WIRE_RejectHandle);
+  rh->rej_cb = rej_cb;
+  rh->rej_cb_cls = rej_cb_cls;
+  rh->timeout_task = GNUNET_SCHEDULER_add_now (&timeout_reject,
+                                               rh);
+  return rh;
+}
+
+
+/**
+ * Cancel ongoing reject operation.  Note that the rejection may still
+ * proceed. Basically, if this function is called, the rejection may
+ * have happened or not.  This function is usually used during shutdown
+ * or system upgrades.  At a later point, the application must call
+ * @e reject_transfer again for this wire transfer, unless the
+ * @e get_history shows that the wire transfer no longer exists.
+ *
+ * @param cls plugins' closure
+ * @param rh operation to cancel
+ * @return closure of the callback of the operation
+ */
+static void *
+sepa_reject_transfer_cancel (void *cls,
+                             struct TALER_WIRE_RejectHandle *rh)
+{
+  void *ret = rh->rej_cb_cls;
+
+  GNUNET_SCHEDULER_cancel (rh->timeout_task);
+  GNUNET_free (rh);
+  return ret;
+}
+
+
 /**
  * Initialize sepa-wire subsystem.
  *
@@ -832,6 +933,8 @@ libtaler_plugin_wire_sepa_init (void *cls)
   plugin->execute_wire_transfer_cancel = &sepa_execute_wire_transfer_cancel;
   plugin->get_history = &sepa_get_history;
   plugin->get_history_cancel = &sepa_get_history_cancel;
+  plugin->reject_transfer = &sepa_reject_transfer;
+  plugin->reject_transfer_cancel = &sepa_reject_transfer_cancel;
   return plugin;
 }
 
diff --git a/src/wire/plugin_wire_test.c b/src/wire/plugin_wire_test.c
index c41bd7e..e9d5ad0 100644
--- a/src/wire/plugin_wire_test.c
+++ b/src/wire/plugin_wire_test.c
@@ -820,52 +820,65 @@ bhist_cb (void *cls,
   uint64_t bserial_id = GNUNET_htonll (serial_id);
   struct TALER_WIRE_TransferDetails wd;
 
-  if (MHD_HTTP_OK == http_status)
-  {
-    char *subject;
-    char *space;
-
-    wd.amount = details->amount;
-    wd.execution_date = details->execution_date;
-    subject = GNUNET_strdup (details->wire_transfer_subject);
-    space = strchr (subject, (int) ' ');
-    if (NULL != space)
-    {
-      /* Space separates the actual wire transfer subject from the
-         exchange base URL (if present, expected only for outgoing
-         transactions).  So we cut the string off at the space. */
-      *space = '\0';
-    }
-    /* NOTE: For a real bank, the subject should include a checksum! */
-    if (GNUNET_OK !=
-        GNUNET_STRINGS_string_to_data (subject,
-                                       strlen (subject),
-                                       &wd.reserve_pub,
-                                       sizeof (wd.reserve_pub)))
+  switch (http_status) {
+  case MHD_HTTP_OK:
     {
-      GNUNET_break (0);
+      char *subject;
+      char *space;
+
+      wd.amount = details->amount;
+      wd.execution_date = details->execution_date;
+      subject = GNUNET_strdup (details->wire_transfer_subject);
+      space = strchr (subject, (int) ' ');
+      if (NULL != space)
+      {
+        /* Space separates the actual wire transfer subject from the
+           exchange base URL (if present, expected only for outgoing
+           transactions).  So we cut the string off at the space. */
+        *space = '\0';
+      }
+      /* NOTE: For a real bank, the subject should include a checksum! */
+      if (GNUNET_OK !=
+          GNUNET_STRINGS_string_to_data (subject,
+                                         strlen (subject),
+                                         &wd.wtid,
+                                         sizeof (wd.wtid)))
+      {
+        /* Ill-formed wire subject, set binary version to all zeros
+           and pass as a string, this time including the part after
+           the space. */
+        memset (&wd.wtid,
+                0,
+                sizeof (wd.wtid));
+        wd.wtid_s = details->wire_transfer_subject;
+      }
       GNUNET_free (subject);
-      /* NOTE: for a "real" bank, we would want to trigger logic to undo the
-         wire transfer. However, for the "demo" bank, it should currently
-         be "impossible" to do wire transfers with invalid subjects, and
-         equally we thus don't need to undo them (and there is no API to do
-         that nicely either right now). So we don't handle this case for now. 
*/
-      return;
+      wd.account_details = details->account_details;
+
+      if ( (NULL != whh->hres_cb) &&
+           (GNUNET_OK !=
+            whh->hres_cb (whh->hres_cb_cls,
+                          dir,
+                          &bserial_id,
+                          sizeof (bserial_id),
+                          &wd)) )
+        whh->hres_cb = NULL;
+      break;
     }
-    GNUNET_free (subject);
-    wd.account_details = details->account_details;
-
-    if ( (NULL != whh->hres_cb) &&
-        (GNUNET_OK !=
-         whh->hres_cb (whh->hres_cb_cls,
-                       dir,
-                       &bserial_id,
-                       sizeof (bserial_id),
-                       &wd)) )
-      whh->hres_cb = NULL;
-  }
-  else
-  {
+  case MHD_HTTP_NO_CONTENT:
+    if (NULL != whh->hres_cb)
+      (void) whh->hres_cb (whh->hres_cb_cls,
+                           TALER_BANK_DIRECTION_NONE,
+                           NULL,
+                           0,
+                           NULL);
+    whh->hh = NULL;
+    GNUNET_free (whh);
+    break;
+  default:
+    /* FIXME: consider modifying API to pass more specific error code(s)
+       back to the application. */
+    GNUNET_break (0);
     if (NULL != whh->hres_cb)
       (void) whh->hres_cb (whh->hres_cb_cls,
                            TALER_BANK_DIRECTION_NONE,
@@ -874,6 +887,7 @@ bhist_cb (void *cls,
                            NULL);
     whh->hh = NULL;
     GNUNET_free (whh);
+    break;
   }
 }
 
@@ -976,6 +990,106 @@ test_get_history_cancel (void *cls,
 
 
 /**
+ * Context for a rejection operation.
+ */
+struct TALER_WIRE_RejectHandle
+{
+  /**
+   * Function to call with the result.
+   */
+  TALER_WIRE_RejectTransferCallback rej_cb;
+
+  /**
+   * Closure for @e rej_cb.
+   */
+  void *rej_cb_cls;
+
+  /**
+   * Handle to task for timeout of operation.
+   */
+  struct GNUNET_SCHEDULER_Task *timeout_task;
+};
+
+
+/**
+ * Rejection operation failed with timeout, notify callback
+ * and clean up.
+ *
+ * @param cls closure with `struct TALER_WIRE_RejectHandle`
+ */
+static void
+timeout_reject (void *cls)
+{
+  struct TALER_WIRE_RejectHandle *rh = cls;
+
+  rh->timeout_task = NULL;
+  rh->rej_cb (rh->rej_cb_cls,
+              TALER_EC_NOT_IMPLEMENTED /* in the future: TALER_EC_TIMEOUT */);
+  GNUNET_free (rh);
+}
+
+
+/**
+ * Reject an incoming wire transfer that was obtained from the
+ * history. This function can be used to transfer funds back to
+ * the sender if the WTID was malformed (i.e. due to a typo).
+ *
+ * Calling `reject_transfer` twice on the same wire transfer should
+ * be idempotent, i.e. not cause the funds to be wired back twice.
+ * Furthermore, the transfer should henceforth be removed from the
+ * results returned by @e get_history.
+ *
+ * @param cls plugin's closure
+ * @param start_off offset of the wire transfer in plugin-specific format
+ * @param start_off_len number of bytes in @a start_off
+ * @param rej_cb function to call with the result of the operation
+ * @param rej_cb_cls closure for @a rej_cb
+ * @return handle to cancel the operation
+ */
+static struct TALER_WIRE_RejectHandle *
+test_reject_transfer (void *cls,
+                      const void *start_off,
+                      size_t start_off_len,
+                      TALER_WIRE_RejectTransferCallback rej_cb,
+                      void *rej_cb_cls)
+{
+  struct TALER_WIRE_RejectHandle *rh;
+
+  GNUNET_break (0); /* not implemented, just a stub! */
+  rh = GNUNET_new (struct TALER_WIRE_RejectHandle);
+  rh->rej_cb = rej_cb;
+  rh->rej_cb_cls = rej_cb_cls;
+  rh->timeout_task = GNUNET_SCHEDULER_add_now (&timeout_reject,
+                                               rh);
+  return rh;
+}
+
+
+/**
+ * Cancel ongoing reject operation.  Note that the rejection may still
+ * proceed. Basically, if this function is called, the rejection may
+ * have happened or not.  This function is usually used during shutdown
+ * or system upgrades.  At a later point, the application must call
+ * @e reject_transfer again for this wire transfer, unless the
+ * @e get_history shows that the wire transfer no longer exists.
+ *
+ * @param cls plugins' closure
+ * @param rh operation to cancel
+ * @return closure of the callback of the operation
+ */
+static void *
+test_reject_transfer_cancel (void *cls,
+                             struct TALER_WIRE_RejectHandle *rh)
+{
+  void *ret = rh->rej_cb_cls;
+
+  GNUNET_SCHEDULER_cancel (rh->timeout_task);
+  GNUNET_free (rh);
+  return ret;
+}
+
+
+/**
  * Initialize test-wire subsystem.
  *
  * @param cls a configuration instance
@@ -1087,6 +1201,8 @@ libtaler_plugin_wire_test_init (void *cls)
   plugin->execute_wire_transfer_cancel = &test_execute_wire_transfer_cancel;
   plugin->get_history = &test_get_history;
   plugin->get_history_cancel = &test_get_history_cancel;
+  plugin->reject_transfer = &test_reject_transfer;
+  plugin->reject_transfer_cancel = &test_reject_transfer_cancel;
   return plugin;
 }
 
diff --git a/src/wire/test_wire_plugin_transactions_test.c 
b/src/wire/test_wire_plugin_transactions_test.c
index ce31e99..26331b5 100644
--- a/src/wire/test_wire_plugin_transactions_test.c
+++ b/src/wire/test_wire_plugin_transactions_test.c
@@ -211,9 +211,8 @@ history_result_cb (void *cls,
     return GNUNET_SYSERR;
   }
   if (0 != memcmp (&wtid,
-                   &details->reserve_pub,
-                   GNUNET_MIN (sizeof (struct TALER_ReservePublicKeyP),
-                               sizeof (wtid))))
+                   &details->wtid,
+                   sizeof (struct TALER_WireTransferIdentifierRawP)))
   {
     GNUNET_break (0);
     global_ret = GNUNET_SYSERR;

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



reply via email to

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