gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] branch master updated: split off taler-helper-auditor-t


From: gnunet
Subject: [taler-exchange] branch master updated: split off taler-helper-auditor-transfer to reduce mixing up different things in helpers; also clean up its code
Date: Fri, 23 Aug 2024 14:16:06 +0200

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

grothoff pushed a commit to branch master
in repository exchange.

The following commit(s) were added to refs/heads/master by this push:
     new 9c21e22da split off taler-helper-auditor-transfer to reduce mixing up 
different things in helpers; also clean up its code
9c21e22da is described below

commit 9c21e22da9e2f411cfc3d2c900f4aabe2e3a88f1
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Fri Aug 23 14:15:18 2024 +0200

    split off taler-helper-auditor-transfer to reduce mixing up different 
things in helpers; also clean up its code
---
 debian/taler-auditor.taler-auditor.target          |    1 +
 ...r-auditor.taler-helper-auditor-transfer.service |   27 +
 src/auditor/.gitignore                             |    1 +
 src/auditor/Makefile.am                            |   15 +-
 ...aler-auditor-httpd_wire-out-inconsistency-get.c |   69 +-
 src/auditor/taler-helper-auditor-transfer.c        |  567 ++++++++++
 src/auditor/taler-helper-auditor-wire-debit.c      | 1084 +++++++-------------
 .../0002-auditor_wire_out_inconsistency.sql        |    7 +-
 src/auditordb/pg_get_wire_format_inconsistency.c   |   52 +-
 src/auditordb/pg_get_wire_out_inconsistency.c      |   78 +-
 .../pg_insert_wire_format_inconsistency.c          |   10 +-
 src/auditordb/pg_insert_wire_out_inconsistency.c   |   21 +-
 src/include/taler_auditordb_plugin.h               |    6 +-
 13 files changed, 1086 insertions(+), 852 deletions(-)

diff --git a/debian/taler-auditor.taler-auditor.target 
b/debian/taler-auditor.taler-auditor.target
index e2e370fdf..324eb625a 100644
--- a/debian/taler-auditor.taler-auditor.target
+++ b/debian/taler-auditor.taler-auditor.target
@@ -8,6 +8,7 @@ Wants=taler-helper-auditor-coins.service
 Wants=taler-helper-auditor-deposits.service
 Wants=taler-helper-auditor-purses.service
 Wants=taler-helper-auditor-reserves.service
+Wants=taler-helper-auditor-transfer.service
 Wants=taler-helper-auditor-wire-credit.service
 Wants=taler-helper-auditor-wire-debit.service
 
diff --git a/debian/taler-auditor.taler-helper-auditor-transfer.service 
b/debian/taler-auditor.taler-helper-auditor-transfer.service
new file mode 100644
index 000000000..9294879cd
--- /dev/null
+++ b/debian/taler-auditor.taler-helper-auditor-transfer.service
@@ -0,0 +1,27 @@
+[Unit]
+Description=GNU Taler auditor helper checking transfers
+After=postgres.service
+PartOf=taler-auditor.target
+
+[Service]
+User=taler-auditor-httpd
+Type=simple
+Restart=always
+RestartSec=1s
+RestartPreventExitStatus=9
+ExecStart=/usr/bin/taler-helper-auditor-transfer -c /etc/taler/taler.conf -L 
INFO
+PrivateTmp=yes
+PrivateDevices=yes
+ProtectSystem=full
+RuntimeMaxSec=3600s
+
+StandardOutput=journal
+StandardError=journal
+Slice=taler-auditor.slice
+
+# Disable the service if more than 5 restarts are encountered within 5s.
+# These are usually the systemd defaults, but can be overwritten, thus we set
+# them here explicitly, as the exchange code assumes StartLimitInterval
+# to be >=5s.
+StartLimitBurst=5
+StartLimitInterval=5s
\ No newline at end of file
diff --git a/src/auditor/.gitignore b/src/auditor/.gitignore
index e7812f058..a0bae7d8b 100644
--- a/src/auditor/.gitignore
+++ b/src/auditor/.gitignore
@@ -29,3 +29,4 @@ generate-kyc-basedb.conf.edited
 generate-auditor-basedb.conf.edited
 wallet.wdb
 libeufin-bank.pid
+taler-helper-auditor-transfer
diff --git a/src/auditor/Makefile.am b/src/auditor/Makefile.am
index 622794b78..1a78cb3a2 100644
--- a/src/auditor/Makefile.am
+++ b/src/auditor/Makefile.am
@@ -23,6 +23,7 @@ bin_PROGRAMS = \
   taler-helper-auditor-deposits \
   taler-helper-auditor-purses \
   taler-helper-auditor-reserves \
+  taler-helper-auditor-transfer \
   taler-helper-auditor-wire-credit \
   taler-helper-auditor-wire-debit
 
@@ -141,7 +142,19 @@ taler_helper_auditor_reserves_LDADD = \
   -lgnunetutil \
   $(XLIB)
 
-
+taler_helper_auditor_transfer_SOURCES = \
+  taler-helper-auditor-transfer.c
+taler_helper_auditor_transfer_LDADD = \
+  $(LIBGCRYPT_LIBS) \
+  $(top_builddir)/src/util/libtalerutil.la \
+  $(top_builddir)/src/json/libtalerjson.la \
+  $(top_builddir)/src/exchangedb/libtalerexchangedb.la \
+  $(top_builddir)/src/auditordb/libtalerauditordb.la \
+  libauditorreport.la \
+  -ljansson \
+  -lgnunetjson \
+  -lgnunetutil \
+  $(XLIB)
 
 taler_helper_auditor_wire_credit_SOURCES = \
   taler-helper-auditor-wire-credit.c
diff --git a/src/auditor/taler-auditor-httpd_wire-out-inconsistency-get.c 
b/src/auditor/taler-auditor-httpd_wire-out-inconsistency-get.c
index 63bcc854b..c7cddceb2 100644
--- a/src/auditor/taler-auditor-httpd_wire-out-inconsistency-get.c
+++ b/src/auditor/taler-auditor-httpd_wire-out-inconsistency-get.c
@@ -13,8 +13,6 @@
    You should have received a copy of the GNU General Public License along with
    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
-
-
 #include "platform.h"
 #include <gnunet/gnunet_util_lib.h>
 #include <gnunet/gnunet_json_lib.h>
@@ -26,38 +24,41 @@
 #include "taler-auditor-httpd.h"
 #include "taler-auditor-httpd_wire-out-inconsistency-get.h"
 
+
 /**
-* Add wire-out-inconsistency to the list.
-*
-* @param[in,out] cls a `json_t *` array to extend
-* @param serial_id location of the @a dc in the database
-* @param dc struct of inconsistencies
-* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop iterating
-*/
+ * Add wire-out-inconsistency to the list.
+ *
+ * @param[in,out] cls a `json_t *` array to extend
+ * @param dc struct of inconsistencies
+ * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop iterating
+ */
 static enum GNUNET_GenericReturnValue
 process_wire_out_inconsistency (
   void *cls,
-  uint64_t serial_id,
   const struct TALER_AUDITORDB_WireOutInconsistency *dc)
 {
   json_t *list = cls;
   json_t *obj;
 
   obj = GNUNET_JSON_PACK (
-
-    GNUNET_JSON_pack_int64 ("row_id", serial_id),
-    GNUNET_JSON_pack_string ("destination_account", dc->destination_account),
-    TALER_JSON_pack_amount ("expected", &dc->expected),
-    TALER_JSON_pack_amount ("claimed", &dc->claimed),
-    GNUNET_JSON_pack_bool ("suppressed", dc->suppressed)
-
-
+    GNUNET_JSON_pack_int64 ("row_id",
+                            dc->row_id),
+    GNUNET_JSON_pack_string ("destination_account",
+                             dc->destination_account),
+    GNUNET_JSON_pack_int64 ("wire_out_row_id",
+                            dc->wire_out_row_id),
+    GNUNET_JSON_pack_string ("diagnostic",
+                             dc->diagnostic),
+    TALER_JSON_pack_amount ("expected",
+                            &dc->expected),
+    TALER_JSON_pack_amount ("claimed",
+                            &dc->claimed),
+    GNUNET_JSON_pack_bool ("suppressed",
+                           dc->suppressed)
     );
   GNUNET_break (0 ==
                 json_array_append_new (list,
                                        obj));
-
-
   return GNUNET_OK;
 }
 
@@ -73,6 +74,9 @@ TAH_WIRE_OUT_INCONSISTENCY_handler_get (
 {
   json_t *ja;
   enum GNUNET_DB_QueryStatus qs;
+  int64_t limit = -20;
+  uint64_t offset;
+  enum TALER_EXCHANGE_YesNoAll return_suppressed;
 
   (void) rh;
   (void) connection_cls;
@@ -87,39 +91,28 @@ TAH_WIRE_OUT_INCONSISTENCY_handler_get (
                                        TALER_EC_GENERIC_DB_SETUP_FAILED,
                                        NULL);
   }
-  ja = json_array ();
-  GNUNET_break (NULL != ja);
-
-  int64_t limit = -20;
-  uint64_t offset;
-
   TALER_MHD_parse_request_snumber (connection,
                                    "limit",
                                    &limit);
-
   if (limit < 0)
     offset = INT64_MAX;
   else
     offset = 0;
-
   TALER_MHD_parse_request_number (connection,
                                   "offset",
                                   &offset);
 
-  bool return_suppressed = false;
-  const char *ret_s = MHD_lookup_connection_value (connection,
-                                                   MHD_GET_ARGUMENT_KIND,
-                                                   "return_suppressed");
-  if (ret_s != NULL && strcmp (ret_s, "true") == 0)
-  {
-    return_suppressed = true;
-  }
-
+  TALER_MHD_parse_request_yna (connection,
+                               "return_suppressed",
+                               false,
+                               &return_suppressed);
+  ja = json_array ();
+  GNUNET_break (NULL != ja);
   qs = TAH_plugin->get_wire_out_inconsistency (
     TAH_plugin->cls,
     limit,
     offset,
-    return_suppressed,
+    (TALER_EXCHANGE_YNA_NO != return_suppressed),
     &process_wire_out_inconsistency,
     ja);
 
diff --git a/src/auditor/taler-helper-auditor-transfer.c 
b/src/auditor/taler-helper-auditor-transfer.c
new file mode 100644
index 000000000..8663ad6f3
--- /dev/null
+++ b/src/auditor/taler-helper-auditor-transfer.c
@@ -0,0 +1,567 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2017-2024 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file auditor/taler-helper-auditor-transfer.c
+ * @brief audits that deposits past due date are
+ *    aggregated and have a matching wire transfer
+ * database.
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler_auditordb_plugin.h"
+#include "taler_exchangedb_lib.h"
+#include "taler_json_lib.h"
+#include "taler_signatures.h"
+#include "report-lib.h"
+#include "taler_dbevents.h"
+
+
+/**
+ * Run in test mode. Exit when idle instead of
+ * going to sleep and waiting for more work.
+ */
+static int test_mode;
+
+/**
+ * Return value from main().
+ */
+static int global_ret;
+
+/**
+ * State of the current database transaction with
+ * the auditor DB.
+ */
+static enum GNUNET_DB_QueryStatus global_qs;
+
+/**
+ * Last reserve_out / wire_out serial IDs seen.
+ */
+static TALER_ARL_DEF_PP (wire_batch_deposit_id);
+static TALER_ARL_DEF_PP (wire_aggregation_id);
+
+/**
+ * Should we run checks that only work for exchange-internal audits?
+ */
+static int internal_checks;
+
+/**
+ * Database event handler to wake us up again.
+ */
+static struct GNUNET_DB_EventHandler *eh;
+
+/**
+ * The auditors's configuration.
+ */
+static const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+
+/**
+ * Task run on shutdown.
+ *
+ * @param cls NULL
+ */
+static void
+do_shutdown (void *cls)
+{
+  (void) cls;
+  if (NULL != eh)
+  {
+    TALER_ARL_adb->event_listen_cancel (eh);
+    eh = NULL;
+  }
+  TALER_ARL_done ();
+  TALER_EXCHANGEDB_unload_accounts ();
+  TALER_ARL_cfg = NULL;
+}
+
+
+/**
+ * Start the database transactions and begin the audit.
+ *
+ * @return false on failure
+ */
+static bool
+begin_transaction (void);
+
+
+/**
+ * Commit the transaction, checkpointing our progress in the auditor DB.
+ *
+ * @param qs transaction status so far
+ * @return transaction status code
+ */
+static void
+commit_transaction (enum GNUNET_DB_QueryStatus qs)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Transaction logic ended with status %d\n",
+              qs);
+  if (qs < 0)
+    goto handle_db_error;
+  qs = TALER_ARL_adb->update_auditor_progress (
+    TALER_ARL_adb->cls,
+    TALER_ARL_SET_PP (wire_batch_deposit_id),
+    TALER_ARL_SET_PP (wire_aggregation_id),
+    NULL);
+  if (0 > qs)
+    goto handle_db_error;
+  qs = TALER_ARL_adb->insert_auditor_progress (
+    TALER_ARL_adb->cls,
+    TALER_ARL_SET_PP (wire_batch_deposit_id),
+    TALER_ARL_SET_PP (wire_aggregation_id),
+    NULL);
+  if (0 > qs)
+    goto handle_db_error;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Concluded audit step at %llu/%llu\n",
+              (unsigned long long) TALER_ARL_USE_PP (wire_aggregation_id),
+              (unsigned long long) TALER_ARL_USE_PP (wire_batch_deposit_id));
+  qs = TALER_ARL_edb->commit (TALER_ARL_edb->cls);
+  if (0 > qs)
+    goto handle_db_error;
+  qs = TALER_ARL_adb->commit (TALER_ARL_adb->cls);
+  if (0 > qs)
+    goto handle_db_error;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Transaction concluded!\n");
+  if (1 == test_mode)
+    GNUNET_SCHEDULER_shutdown ();
+  return;
+handle_db_error:
+  TALER_ARL_adb->rollback (TALER_ARL_adb->cls);
+  TALER_ARL_edb->rollback (TALER_ARL_edb->cls);
+  if (GNUNET_DB_STATUS_HARD_ERROR != qs)
+  {
+    for (unsigned int max_retries = 3; max_retries>0; max_retries--)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Trying again (%u attempts left)\n",
+                  max_retries);
+      if (begin_transaction ())
+        return;
+    }
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+              "Hard database error, terminating\n");
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
+ * Closure for import_wire_missing_cb().
+ */
+struct ImportMissingWireContext
+{
+  /**
+   * Set to maximum row ID encountered.
+   */
+  uint64_t max_batch_deposit_uuid;
+
+  /**
+   * Set to database errors in callback.
+   */
+  enum GNUNET_DB_QueryStatus err;
+};
+
+
+/**
+ * Function called on deposits that need to be checked for their
+ * wire transfer.
+ *
+ * @param cls closure, points to a `struct ImportMissingWireContext`
+ * @param batch_deposit_serial_id serial of the entry in the batch deposits 
table
+ * @param total_amount value of the missing deposits, including fee
+ * @param wire_target_h_payto where should the funds be wired
+ * @param deadline what was the earliest requested wire transfer deadline
+ */
+static void
+import_wire_missing_cb (
+  void *cls,
+  uint64_t batch_deposit_serial_id,
+  const struct TALER_Amount *total_amount,
+  const struct TALER_PaytoHashP *wire_target_h_payto,
+  struct GNUNET_TIME_Timestamp deadline)
+{
+  struct ImportMissingWireContext *wc = cls;
+  enum GNUNET_DB_QueryStatus qs;
+
+  if (wc->err < 0)
+    return; /* already failed */
+  GNUNET_assert (batch_deposit_serial_id > wc->max_batch_deposit_uuid);
+  wc->max_batch_deposit_uuid = batch_deposit_serial_id;
+  qs = TALER_ARL_adb->insert_pending_deposit (
+    TALER_ARL_adb->cls,
+    batch_deposit_serial_id,
+    wire_target_h_payto,
+    total_amount,
+    deadline);
+  if (qs < 0)
+    wc->err = qs;
+}
+
+
+/**
+ * Closure for #clear_finished_transfer_cb().
+ */
+struct AggregationContext
+{
+  /**
+   * Set to maximum row ID encountered.
+   */
+  uint64_t max_aggregation_serial;
+
+  /**
+   * Set to database errors in callback.
+   */
+  enum GNUNET_DB_QueryStatus err;
+};
+
+
+/**
+ * Function called on aggregations that were done for
+ * a (batch) deposit.
+ *
+ * @param cls closure
+ * @param tracking_serial_id where in the table are we
+ * @param batch_deposit_serial_id which batch deposit was aggregated
+ */
+static void
+clear_finished_transfer_cb (
+  void *cls,
+  uint64_t tracking_serial_id,
+  uint64_t batch_deposit_serial_id)
+{
+  struct AggregationContext *ac = cls;
+  enum GNUNET_DB_QueryStatus qs;
+
+  if (0 > ac->err)
+    return; /* already failed */
+  GNUNET_assert (ac->max_aggregation_serial < tracking_serial_id);
+  ac->max_aggregation_serial = tracking_serial_id;
+  qs = TALER_ARL_adb->delete_pending_deposit (
+    TALER_ARL_adb->cls,
+    batch_deposit_serial_id);
+  if (0 == qs)
+  {
+    /* Aggregated something twice or other error, report! */
+    GNUNET_break (0);
+    // FIXME: report more nicely!
+  }
+  if (0 > qs)
+    ac->err = qs;
+}
+
+
+/**
+ * Checks for wire transfers that should have happened.
+ *
+ * @return false on failure
+ */
+static bool
+check_for_required_transfers (void)
+{
+  enum GNUNET_DB_QueryStatus qs;
+  struct ImportMissingWireContext wc = {
+    .max_batch_deposit_uuid = TALER_ARL_USE_PP (wire_batch_deposit_id),
+    .err = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
+  };
+
+  qs = TALER_ARL_edb->select_batch_deposits_missing_wire (
+    TALER_ARL_edb->cls,
+    TALER_ARL_USE_PP (wire_batch_deposit_id),
+    &import_wire_missing_cb,
+    &wc);
+  if ((0 > qs) || (0 > wc.err))
+  {
+    GNUNET_break (0);
+    GNUNET_break ((GNUNET_DB_STATUS_SOFT_ERROR == qs) ||
+                  (GNUNET_DB_STATUS_SOFT_ERROR == wc.err));
+    global_ret = EXIT_FAILURE;
+    GNUNET_SCHEDULER_shutdown ();
+    return false;
+  }
+  TALER_ARL_USE_PP (wire_batch_deposit_id) = wc.max_batch_deposit_uuid;
+  return true;
+}
+
+
+/**
+ * Checks that all wire transfers that should have happened
+ * (based on deposits) have indeed happened.
+ *
+ * @return false on failure
+ */
+static bool
+check_for_completed_transfers (void)
+{
+  struct AggregationContext ac = {
+    .max_aggregation_serial = TALER_ARL_USE_PP (wire_aggregation_id),
+    .err = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  qs = TALER_ARL_edb->select_aggregations_above_serial (
+    TALER_ARL_edb->cls,
+    TALER_ARL_USE_PP (wire_aggregation_id),
+    &clear_finished_transfer_cb,
+    &ac);
+  if ( (0 > qs) || (0 > ac.err) )
+  {
+    GNUNET_break (0);
+    GNUNET_break ((GNUNET_DB_STATUS_SOFT_ERROR == qs) ||
+                  (GNUNET_DB_STATUS_SOFT_ERROR == ac.err));
+    global_ret = EXIT_FAILURE;
+    GNUNET_SCHEDULER_shutdown ();
+    return false;
+  }
+  TALER_ARL_USE_PP (wire_aggregation_id) = ac.max_aggregation_serial;
+  return true;
+}
+
+
+/**
+ * Start the database transactions and begin the audit.
+ *
+ * @return true on success
+ */
+static bool
+begin_transaction (void)
+{
+  enum GNUNET_DB_QueryStatus qs;
+
+  if (GNUNET_SYSERR ==
+      TALER_ARL_edb->preflight (TALER_ARL_edb->cls))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Failed to initialize exchange database connection.\n");
+    return false;
+  }
+  if (GNUNET_SYSERR ==
+      TALER_ARL_adb->preflight (TALER_ARL_adb->cls))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Failed to initialize auditor database session.\n");
+    return false;
+  }
+  global_qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+  if (GNUNET_OK !=
+      TALER_ARL_adb->start (TALER_ARL_adb->cls))
+  {
+    GNUNET_break (0);
+    return false;
+  }
+  TALER_ARL_edb->preflight (TALER_ARL_edb->cls);
+  if (GNUNET_OK !=
+      TALER_ARL_edb->start (TALER_ARL_edb->cls,
+                            "transfer auditor"))
+  {
+    GNUNET_break (0);
+    TALER_ARL_adb->rollback (TALER_ARL_adb->cls);
+    return false;
+  }
+  qs = TALER_ARL_adb->get_auditor_progress (
+    TALER_ARL_adb->cls,
+    TALER_ARL_GET_PP (wire_batch_deposit_id),
+    TALER_ARL_GET_PP (wire_aggregation_id),
+    NULL);
+  if (0 > qs)
+  {
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    TALER_ARL_adb->rollback (TALER_ARL_adb->cls);
+    TALER_ARL_edb->rollback (TALER_ARL_edb->cls);
+    return false;
+  }
+
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+                "First analysis of with transfer auditor, starting audit from 
scratch\n");
+  }
+  else
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Resuming transfer audit at %llu / %llu\n",
+                (unsigned long long) TALER_ARL_USE_PP (wire_batch_deposit_id),
+                (unsigned long long) TALER_ARL_USE_PP (wire_aggregation_id));
+  }
+
+  {
+    bool ok;
+
+    ok = check_for_required_transfers ();
+    if (ok)
+      ok = check_for_completed_transfers ();
+    commit_transaction (global_qs);
+    if (test_mode)
+    {
+      GNUNET_SCHEDULER_shutdown ();
+      return ok;
+    }
+    return ok;
+  }
+}
+
+
+/**
+ * Function called on events received from Postgres.
+ *
+ * @param cls closure, NULL
+ * @param extra additional event data provided
+ * @param extra_size number of bytes in @a extra
+ */
+static void
+db_notify (void *cls,
+           const void *extra,
+           size_t extra_size)
+{
+  (void) cls;
+  (void) extra;
+  (void) extra_size;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Received notification to wake transfer helper\n");
+  if (! begin_transaction ())
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Audit failed\n");
+    GNUNET_break (0);
+    global_ret = EXIT_FAILURE;
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+}
+
+
+/**
+ * Main function that will be run.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be 
NULL!)
+ * @param c configuration
+ */
+static void
+run (void *cls,
+     char *const *args,
+     const char *cfgfile,
+     const struct GNUNET_CONFIGURATION_Handle *c)
+{
+  (void) cls;
+  (void) args;
+  (void) cfgfile;
+  cfg = c;
+  if (GNUNET_OK !=
+      TALER_ARL_init (c))
+  {
+    global_ret = EXIT_FAILURE;
+    return;
+  }
+  GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
+                                 NULL);
+  if (GNUNET_OK !=
+      TALER_EXCHANGEDB_load_accounts (TALER_ARL_cfg,
+                                      TALER_EXCHANGEDB_ALO_DEBIT
+                                      | TALER_EXCHANGEDB_ALO_CREDIT
+                                      | TALER_EXCHANGEDB_ALO_AUTHDATA))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "No bank accounts configured\n");
+    global_ret = EXIT_NOTCONFIGURED;
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  if (0 == test_mode)
+  {
+    // FIXME: use different event type in the future!
+    struct GNUNET_DB_EventHeaderP es = {
+      .size = htons (sizeof (es)),
+      .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_WIRE)
+    };
+
+    eh = TALER_ARL_adb->event_listen (TALER_ARL_adb->cls,
+                                      &es,
+                                      GNUNET_TIME_UNIT_FOREVER_REL,
+                                      &db_notify,
+                                      NULL);
+    GNUNET_assert (NULL != eh);
+  }
+  if (! begin_transaction ())
+  {
+    GNUNET_break (0);
+    global_ret = EXIT_FAILURE;
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+}
+
+
+/**
+ * The main function of the wire auditing tool. Checks that
+ * the exchange's records of wire transfers match that of
+ * the wire gateway.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc,
+      char *const *argv)
+{
+  const struct GNUNET_GETOPT_CommandLineOption options[] = {
+    GNUNET_GETOPT_option_flag ('i',
+                               "internal",
+                               "perform checks only applicable for 
exchange-internal audits",
+                               &internal_checks),
+    GNUNET_GETOPT_option_flag ('t',
+                               "test",
+                               "run in test mode and exit when idle",
+                               &test_mode),
+    GNUNET_GETOPT_option_timetravel ('T',
+                                     "timetravel"),
+    GNUNET_GETOPT_OPTION_END
+  };
+  enum GNUNET_GenericReturnValue ret;
+
+  /* force linker to link against libtalerutil; if we do
+     not do this, the linker may "optimize" libtalerutil
+     away and skip #TALER_OS_init(), which we do need */
+  (void) TALER_project_data_default ();
+  if (GNUNET_OK !=
+      GNUNET_STRINGS_get_utf8_args (argc, argv,
+                                    &argc, &argv))
+    return EXIT_INVALIDARGUMENT;
+  ret = GNUNET_PROGRAM_run (
+    argc,
+    argv,
+    "taler-helper-auditor-transfer",
+    gettext_noop (
+      "Audit exchange database for consistency of transfers with respect to 
deposit deadlines"),
+    options,
+    &run,
+    NULL);
+  GNUNET_free_nz ((void *) argv);
+  if (GNUNET_SYSERR == ret)
+    return EXIT_INVALIDARGUMENT;
+  if (GNUNET_NO == ret)
+    return EXIT_SUCCESS;
+  return global_ret;
+}
+
+
+/* end of taler-helper-auditor-transfer.c */
diff --git a/src/auditor/taler-helper-auditor-wire-debit.c 
b/src/auditor/taler-helper-auditor-wire-debit.c
index c9b1157c8..105867bf2 100644
--- a/src/auditor/taler-helper-auditor-wire-debit.c
+++ b/src/auditor/taler-helper-auditor-wire-debit.c
@@ -21,9 +21,14 @@
  * @author Özgür Kesim
  *
  * - We check that the outgoing wire transfers match those
- *   given in the 'wire_out' and 'reserve_closures' tables
- * - Finally, we check that all wire transfers that should have been made,
- *   were actually made
+ *   given in the 'wire_out' and 'reserve_closures' tables;
+ *   any outgoing transfer MUST have a prior justification,
+ *   so if one is missing we flag it (and never remove it).
+ * - We check that all wire transfers that should
+ *   have been made, were actually made. If any were not made,
+ *   we flag those, but may remove those flags if we later
+ *   find that the wire transfers were made (wire transfers
+ *   could be delayed due to AML/KYC or core-banking issues).
  */
 #include "platform.h"
 #include <gnunet/gnunet_util_lib.h>
@@ -44,6 +49,12 @@
  */
 #define GRACE_PERIOD GNUNET_TIME_UNIT_HOURS
 
+/**
+ * Maximum number of wire transfers we process per
+ * (database) transaction.
+ */
+#define MAX_PER_TRANSACTION 1024
+
 /**
  * How much do we allow the bank and the exchange to disagree about
  * timestamps? Should be sufficiently large to avoid bogus reports from deltas
@@ -53,6 +64,13 @@
                                                       15)
 
 
+/**
+ * How long do we long-poll for bank wire transfers?
+ */
+#define LONG_POLL_MAX GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, \
+                                                     1)
+
+
 /**
  * Run in test mode. Exit when idle instead of
  * going to sleep and waiting for more work.
@@ -181,8 +199,6 @@ static struct WireAccount *wa_tail;
  * Last reserve_out / wire_out serial IDs seen.
  */
 static TALER_ARL_DEF_PP (wire_reserve_close_id);
-static TALER_ARL_DEF_PP (wire_batch_deposit_id);
-static TALER_ARL_DEF_PP (wire_aggregation_id);
 
 /**
  * Amount that is considered "tiny"
@@ -210,9 +226,10 @@ static TALER_ARL_DEF_AB (total_amount_lag);
 static TALER_ARL_DEF_AB (total_closure_amount_lag);
 
 /**
- * Total amount affected by wire format troubles.
+ * Total amount affected by duplicate wire transfer
+ * subjects.
  */
-static TALER_ARL_DEF_AB (total_wire_format_amount);
+static TALER_ARL_DEF_AB (wire_debit_duplicate_transfer_subject_total);
 
 /**
  * Total amount debited to exchange accounts.
@@ -502,7 +519,7 @@ commit (enum GNUNET_DB_QueryStatus qs)
     TALER_ARL_SET_AB (total_bad_amount_out_minus),
     TALER_ARL_SET_AB (total_amount_lag),
     TALER_ARL_SET_AB (total_closure_amount_lag),
-    TALER_ARL_SET_AB (total_wire_format_amount),
+    TALER_ARL_SET_AB (wire_debit_duplicate_transfer_subject_total),
     TALER_ARL_SET_AB (total_wire_out),
     NULL);
   if (0 > qs)
@@ -515,7 +532,7 @@ commit (enum GNUNET_DB_QueryStatus qs)
     TALER_ARL_SET_AB (total_bad_amount_out_minus),
     TALER_ARL_SET_AB (total_amount_lag),
     TALER_ARL_SET_AB (total_closure_amount_lag),
-    TALER_ARL_SET_AB (total_wire_format_amount),
+    TALER_ARL_SET_AB (wire_debit_duplicate_transfer_subject_total),
     TALER_ARL_SET_AB (total_wire_out),
     NULL);
   if (0 > qs)
@@ -554,23 +571,18 @@ commit (enum GNUNET_DB_QueryStatus qs)
   qs = TALER_ARL_adb->update_auditor_progress (
     TALER_ARL_adb->cls,
     TALER_ARL_SET_PP (wire_reserve_close_id),
-    TALER_ARL_SET_PP (wire_batch_deposit_id),
-    TALER_ARL_SET_PP (wire_aggregation_id),
     NULL);
   if (0 > qs)
     goto handle_db_error;
   qs = TALER_ARL_adb->insert_auditor_progress (
     TALER_ARL_adb->cls,
     TALER_ARL_SET_PP (wire_reserve_close_id),
-    TALER_ARL_SET_PP (wire_batch_deposit_id),
-    TALER_ARL_SET_PP (wire_aggregation_id),
     NULL);
   if (0 > qs)
     goto handle_db_error;
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Concluded audit step at %llu/%llu\n",
-              (unsigned long long) TALER_ARL_USE_PP (wire_aggregation_id),
-              (unsigned long long) TALER_ARL_USE_PP (wire_batch_deposit_id));
+              "Concluded audit step at %llu\n",
+              (unsigned long long) TALER_ARL_USE_PP (wire_reserve_close_id));
   qs = TALER_ARL_edb->commit (TALER_ARL_edb->cls);
   if (0 > qs)
     goto handle_db_error;
@@ -599,440 +611,335 @@ handle_db_error:
 }
 
 
-/* ******************** Analyze required outgoing transfers 
******************** */
-
-/**
- * Closure for import_wire_missing_cb().
- */
-struct ImportMissingWireContext
-{
-  /**
-   * Set to maximum row ID encountered.
-   */
-  uint64_t max_batch_deposit_uuid;
-
-  /**
-   * Set to database errors in callback.
-   */
-  enum GNUNET_DB_QueryStatus err;
-};
-
-
 /**
- * Function called on deposits that need to be checked for their
- * wire transfer.
+ * Check that @a want is within #TIME_TOLERANCE of @a have.
+ * Otherwise report an inconsistency in row @a rowid of @a table.
  *
- * @param cls closure, points to a `struct ImportMissingWireContext`
- * @param batch_deposit_serial_id serial of the entry in the batch deposits 
table
- * @param total_amount value of the missing deposits, including fee
- * @param wire_target_h_payto where should the funds be wired
- * @param deadline what was the earliest requested wire transfer deadline
+ * @param table where is the inconsistency (if any)
+ * @param rowid what is the row
+ * @param want what is the expected time
+ * @param have what is the time we got
+ * @return true on success, false to abort
  */
-static void
-import_wire_missing_cb (
-  void *cls,
-  uint64_t batch_deposit_serial_id,
-  const struct TALER_Amount *total_amount,
-  const struct TALER_PaytoHashP *wire_target_h_payto,
-  struct GNUNET_TIME_Timestamp deadline)
+static bool
+check_time_difference (const char *table,
+                       uint64_t rowid,
+                       struct GNUNET_TIME_Timestamp want,
+                       struct GNUNET_TIME_Timestamp have)
 {
-  struct ImportMissingWireContext *wc = cls;
-  enum GNUNET_DB_QueryStatus qs;
-
-  if (wc->err < 0)
-    return; /* already failed */
-  GNUNET_assert (batch_deposit_serial_id > wc->max_batch_deposit_uuid);
-  wc->max_batch_deposit_uuid = batch_deposit_serial_id;
-  qs = TALER_ARL_adb->insert_pending_deposit (
-    TALER_ARL_adb->cls,
-    batch_deposit_serial_id,
-    wire_target_h_payto,
-    total_amount,
-    deadline);
-  if (qs < 0)
-    wc->err = qs;
-}
-
+  struct GNUNET_TIME_Relative delta;
+  char *details;
 
-/**
- * Information about a delayed wire transfer and the possible
- * reasons for the delay.
- */
-struct ReasonDetail
-{
-  /**
-   * Batch deposit that may be lacking a wire transfer.
-   */
-  uint64_t batch_deposit_serial_id;
+  if (GNUNET_TIME_timestamp_cmp (have, >, want))
+    delta = GNUNET_TIME_absolute_get_difference (want.abs_time,
+                                                 have.abs_time);
+  else
+    delta = GNUNET_TIME_absolute_get_difference (have.abs_time,
+                                                 want.abs_time);
+  if (GNUNET_TIME_relative_cmp (delta,
+                                <=,
+                                TIME_TOLERANCE))
+    return true;
 
-  /**
-   * Total amount that should have been transferred.
-   */
-  struct TALER_Amount total_amount;
+  GNUNET_asprintf (&details,
+                   "execution date mismatch (%s)",
+                   GNUNET_TIME_relative2s (delta,
+                                           true));
+  {
+    struct TALER_AUDITORDB_RowMinorInconsistencies rmi = {
+      .row_id = rowid,
+      .diagnostic = details,
+      .row_table = (char *) table
+    };
+    enum GNUNET_DB_QueryStatus qs;
 
-  /**
-   * Earliest deadline for an expected transfer to the account.
-   */
-  struct GNUNET_TIME_Timestamp deadline;
+    qs = TALER_ARL_adb->insert_row_minor_inconsistencies (
+      TALER_ARL_adb->cls,
+      &rmi);
 
-  /**
-   * Target account hash.
-   */
-  struct TALER_PaytoHashP wire_target_h_payto;
+    if (qs < 0)
+    {
+      global_qs = qs;
+      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+      GNUNET_free (details);
+      return false;
+    }
+  }
+  GNUNET_free (details);
+  return true;
+}
 
-};
 
 /**
- * Closure for report_wire_missing_cb().
+ * Closure for #check_rc_matches
  */
-struct ReportMissingWireContext
+struct CheckMatchContext
 {
-  /**
-   * Map from wire_target_h_payto to `struct ReasonDetail`.
-   */
-  struct GNUNET_CONTAINER_MultiShortmap *map;
 
   /**
-   * Set to database errors in callback.
-   */
-  enum GNUNET_DB_QueryStatus err;
-};
-
-
-/**
- * Closure for #clear_finished_transfer_cb().
- */
-struct AggregationContext
-{
-  /**
-   * Set to maximum row ID encountered.
+   * Reserve operation looking for a match
    */
-  uint64_t max_aggregation_serial;
+  const struct ReserveOutInfo *roi;
 
   /**
-   * Set to database errors in callback.
+   * Set to true if we found a match.
    */
-  enum GNUNET_DB_QueryStatus err;
+  bool found;
 };
 
 
 /**
- * Free memory allocated in @a value.
- *
- * @param cls unused
- * @param key unused
- * @param value must be a `struct ReasonDetail`
- * @return #GNUNET_YES if we should continue to
- *         iterate,
- *         #GNUNET_NO if not.
- */
-static enum GNUNET_GenericReturnValue
-free_report_entry (void *cls,
-                   const struct GNUNET_ShortHashCode *key,
-                   void *value)
-{
-  struct ReasonDetail *rd = value;
-
-  GNUNET_free (rd);
-  return GNUNET_YES;
-}
-
-
-/**
- * We had an entry in our map of wire transfers that
- * should have been performed. Generate report.
+ * Check if any of the reserve closures match the given wire transfer.
  *
- * @param cls unused
- * @param key unused
- * @param value must be a `struct ReasonDetail`
- * @return #GNUNET_YES if we should continue to
- *         iterate,
- *         #GNUNET_NO if not.
+ * @param[in,out] cls a `struct CheckMatchContext`
+ * @param key key of @a value in #reserve_closures
+ * @param value a `struct ReserveClosure`
  */
 static enum GNUNET_GenericReturnValue
-generate_report (void *cls,
-                 const struct GNUNET_ShortHashCode *key,
-                 void *value)
+check_rc_matches (void *cls,
+                  const struct GNUNET_HashCode *key,
+                  void *value)
 {
-  struct ReasonDetail *rd = value;
-
-
-  /* For now, we simplify and only check that the
-     amount was tiny */
-  if (0 > TALER_amount_cmp (&rd->total_amount,
-                            &tiny_amount))
-    return free_report_entry (cls,
-                              key,
-                              value); /* acceptable, amount was tiny */
+  struct CheckMatchContext *ctx = cls;
+  struct ReserveClosure *rc = value;
 
-  // TODO: maybe split total_amount_lag up by category below?
-  TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_amount_lag),
-                        &TALER_ARL_USE_AB (total_amount_lag),
-                        &rd->total_amount);
+  if ((0 == GNUNET_memcmp (&ctx->roi->details.wtid,
+                           &rc->wtid)) &&
+      (0 == strcasecmp (rc->receiver_account,
+                        ctx->roi->details.credit_account_uri)) &&
+      (0 == TALER_amount_cmp (&rc->amount,
+                              &ctx->roi->details.amount)))
   {
-    enum GNUNET_DB_QueryStatus qs;
-
-    qs = TALER_ARL_adb->insert_pending_deposit (
-      TALER_ARL_adb->cls,
-      rd->batch_deposit_serial_id,
-      &rd->wire_target_h_payto,
-      &rd->total_amount,
-      rd->deadline);
-    if (qs < 0)
+    if (! check_time_difference ("reserves_closures",
+                                 rc->rowid,
+                                 rc->execution_date,
+                                 ctx->roi->details.execution_date))
     {
-      global_qs = qs;
-      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+      free_rc (NULL,
+               key,
+               rc);
       return GNUNET_SYSERR;
     }
+    ctx->found = true;
+    free_rc (NULL,
+             key,
+             rc);
+    return GNUNET_NO;
   }
-  return free_report_entry (cls,
-                            key,
-                            value);
-}
-
-
-/**
- * Function called on deposits that are past their due date
- * and have not yet seen a wire transfer.
- *
- * @param cls closure, points to a `struct ReportMissingWireContext`
- * @param batch_deposit_serial_id row in the database for which the wire 
transfer is missing
- * @param total_amount value of the missing deposits, including fee
- * @param wire_target_h_payto hash of payto-URI where the funds should have 
been wired
- * @param deadline what was the earliest requested wire transfer deadline
- */
-static void
-report_wire_missing_cb (
-  void *cls,
-  uint64_t batch_deposit_serial_id,
-  const struct TALER_Amount *total_amount,
-  const struct TALER_PaytoHashP *wire_target_h_payto,
-  struct GNUNET_TIME_Timestamp deadline)
-{
-  struct ReportMissingWireContext *rc = cls;
-  struct ReasonDetail *rd;
-
-  rd = GNUNET_CONTAINER_multishortmap_get (rc->map,
-                                           &wire_target_h_payto->hash);
-  if (NULL == rd)
-  {
-    rd = GNUNET_new (struct ReasonDetail);
-    rd->batch_deposit_serial_id = batch_deposit_serial_id;
-    rd->wire_target_h_payto = *wire_target_h_payto;
-    rd->total_amount = *total_amount;
-    rd->deadline = deadline;
-    GNUNET_assert (GNUNET_YES ==
-                   GNUNET_CONTAINER_multishortmap_put (
-                     rc->map,
-                     &wire_target_h_payto->hash,
-                     rd,
-                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
-  }
-  else
-  {
-    TALER_ARL_amount_add (&rd->total_amount,
-                          &rd->total_amount,
-                          total_amount);
-    rd->deadline = GNUNET_TIME_timestamp_min (rd->deadline,
-                                              deadline);
-  }
+  return GNUNET_OK;
 }
 
 
 /**
- * Function called on aggregations that were done for
- * a (batch) deposit.
+ * Check if a profit drain operation justified the @a roi
  *
- * @param cls closure
- * @param tracking_serial_id where in the table are we
- * @param batch_deposit_serial_id which batch deposit was aggregated
- */
-static void
-clear_finished_transfer_cb (
-  void *cls,
-  uint64_t tracking_serial_id,
-  uint64_t batch_deposit_serial_id)
-{
-  struct AggregationContext *ac = cls;
-  enum GNUNET_DB_QueryStatus qs;
-
-  if (0 > ac->err)
-    return; /* already failed */
-  GNUNET_assert (ac->max_aggregation_serial < tracking_serial_id);
-  ac->max_aggregation_serial = tracking_serial_id;
-  qs = TALER_ARL_adb->delete_pending_deposit (
-    TALER_ARL_adb->cls,
-    batch_deposit_serial_id);
-  if (0 == qs)
-  {
-    /* Aggregated something twice or other error, report! */
-    GNUNET_break (0);
-    // FIXME: report more nicely!
-  }
-  if (0 > qs)
-    ac->err = qs;
-}
-
-
-/**
- * Checks that all wire transfers that should have happened
- * (based on deposits) have indeed happened.
+ * @param roi reserve out operation to check
+ * @return #GNUNET_YES if @a roi was justified by a profit drain,
+ *         #GNUNET_NO of @a roi was not justified by a proft drain
+ *         #GNUNET_SYSERR on database trouble
  */
-static void
-check_for_required_transfers (void)
+static enum GNUNET_GenericReturnValue
+check_profit_drain (struct ReserveOutInfo *roi)
 {
-  struct ImportMissingWireContext wc = {
-    .max_batch_deposit_uuid = TALER_ARL_USE_PP (wire_batch_deposit_id),
-    .err = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
-  };
-  struct GNUNET_TIME_Absolute deadline;
   enum GNUNET_DB_QueryStatus qs;
-  struct ReportMissingWireContext rc = {
-    .err = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
-  };
-  struct AggregationContext ac = {
-    .max_aggregation_serial = TALER_ARL_USE_PP (wire_aggregation_id),
-    .err = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
-  };
+  uint64_t serial;
+  char *account_section;
+  char *payto_uri;
+  struct GNUNET_TIME_Timestamp request_timestamp;
+  struct TALER_Amount amount;
+  struct TALER_MasterSignatureP master_sig;
 
-  qs = TALER_ARL_edb->select_batch_deposits_missing_wire (
+  qs = TALER_ARL_edb->get_drain_profit (
     TALER_ARL_edb->cls,
-    TALER_ARL_USE_PP (wire_batch_deposit_id),
-    &import_wire_missing_cb,
-    &wc);
-  if ((0 > qs) || (0 > wc.err))
+    &roi->details.wtid,
+    &serial,
+    &account_section,
+    &payto_uri,
+    &request_timestamp,
+    &amount,
+    &master_sig);
+  switch (qs)
   {
+  case GNUNET_DB_STATUS_HARD_ERROR:
     GNUNET_break (0);
-    GNUNET_break ((GNUNET_DB_STATUS_SOFT_ERROR == qs) ||
-                  (GNUNET_DB_STATUS_SOFT_ERROR == wc.err));
     global_ret = EXIT_FAILURE;
     GNUNET_SCHEDULER_shutdown ();
-    return;
-  }
-  TALER_ARL_USE_PP (wire_batch_deposit_id) = wc.max_batch_deposit_uuid;
-  qs = TALER_ARL_edb->select_aggregations_above_serial (
-    TALER_ARL_edb->cls,
-    TALER_ARL_USE_PP (wire_aggregation_id),
-    &clear_finished_transfer_cb,
-    &ac);
-  if ((0 > qs) || (0 > ac.err))
-  {
+    return GNUNET_SYSERR;
+  case GNUNET_DB_STATUS_SOFT_ERROR:
+    /* should fail on commit later ... */
     GNUNET_break (0);
-    GNUNET_break ((GNUNET_DB_STATUS_SOFT_ERROR == qs) ||
-                  (GNUNET_DB_STATUS_SOFT_ERROR == ac.err));
-    global_ret = EXIT_FAILURE;
-    GNUNET_SCHEDULER_shutdown ();
-    return;
+    return GNUNET_SYSERR;
+  case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+    /* not a profit drain */
+    return GNUNET_NO;
+  case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+    break;
   }
-  TALER_ARL_USE_PP (wire_aggregation_id) = ac.max_aggregation_serial;
-  /* Subtract #GRACE_PERIOD, so we can be a bit behind in processing
-     without immediately raising undue concern */
-  deadline = GNUNET_TIME_absolute_subtract (GNUNET_TIME_absolute_get (),
-                                            GRACE_PERIOD);
-  rc.map = GNUNET_CONTAINER_multishortmap_create (1024,
-                                                  GNUNET_NO);
-  qs = TALER_ARL_adb->select_pending_deposits (
-    TALER_ARL_adb->cls,
-    deadline,
-    &report_wire_missing_cb,
-    &rc);
-  if ((0 > qs) || (0 > rc.err))
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Profit drain of %s to %s found!\n",
+              TALER_amount2s (&amount),
+              payto_uri);
+  if (GNUNET_OK !=
+      TALER_exchange_offline_profit_drain_verify (
+        &roi->details.wtid,
+        request_timestamp,
+        &amount,
+        account_section,
+        payto_uri,
+        &TALER_ARL_master_pub,
+        &master_sig))
   {
+    struct TALER_AUDITORDB_RowInconsistency ri = {
+      .row_id = roi->details.serial_id,
+      .row_table = "profit_drains",
+      .diagnostic = "invalid signature"
+    };
+
     GNUNET_break (0);
-    GNUNET_break ((GNUNET_DB_STATUS_SOFT_ERROR == qs) ||
-                  (GNUNET_DB_STATUS_SOFT_ERROR == rc.err));
-    GNUNET_CONTAINER_multishortmap_iterate (rc.map,
-                                            &free_report_entry,
-                                            NULL);
-    GNUNET_CONTAINER_multishortmap_destroy (rc.map);
-    global_ret = EXIT_FAILURE;
-    GNUNET_SCHEDULER_shutdown ();
-    return;
+    qs = TALER_ARL_adb->insert_row_inconsistency (
+      TALER_ARL_adb->cls,
+      &ri);
+    GNUNET_free (payto_uri);
+    GNUNET_free (account_section);
+    if (qs < 0)
+    {
+      global_qs = qs;
+      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+      return GNUNET_SYSERR;
+    }
+    return GNUNET_NO;
   }
-  GNUNET_CONTAINER_multishortmap_iterate (rc.map,
-                                          &generate_report,
-                                          NULL);
-  GNUNET_CONTAINER_multishortmap_destroy (rc.map);
-  /* conclude with success */
-  commit (global_qs);
-  if (test_mode)
+  GNUNET_free (account_section);
+  if (0 !=
+      strcasecmp (payto_uri,
+                  roi->details.credit_account_uri))
   {
-    GNUNET_SCHEDULER_shutdown ();
-    return;
-  }
-}
-
+    struct TALER_AUDITORDB_WireOutInconsistency woi = {
+      .row_id = serial,
+      .destination_account = (char *) roi->details.credit_account_uri,
+      .diagnostic = "profit drain wired to invalid account",
+      .expected = roi->details.amount,
+      .claimed = zero,
+    };
 
-/* ***************************** Analyze reserves_out ************************ 
*/
+    qs = TALER_ARL_adb->insert_wire_out_inconsistency (
+      TALER_ARL_adb->cls,
+      &woi);
+    if (qs < 0)
+    {
+      global_qs = qs;
+      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+      GNUNET_free (payto_uri);
+      return GNUNET_SYSERR;
+    }
+    TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus),
+                          &TALER_ARL_USE_AB (total_bad_amount_out_plus),
+                          &amount);
+    GNUNET_free (payto_uri);
+    return GNUNET_YES; /* justified, kind-of */
+  }
+  GNUNET_free (payto_uri);
+  if (0 !=
+      TALER_amount_cmp (&amount,
+                        &roi->details.amount))
+  {
+    struct TALER_AUDITORDB_WireOutInconsistency woi = {
+      .row_id = roi->details.serial_id,
+      .destination_account = (char *) roi->details.credit_account_uri,
+      .diagnostic = "incorrect amount drained to correct account",
+      .expected = roi->details.amount,
+      .claimed = amount,
+    };
 
-/**
- * Clean up after processing wire out data.
- */
-static void
-conclude_wire_out (void)
-{
-  GNUNET_CONTAINER_multihashmap_destroy (out_map);
-  out_map = NULL;
-  check_for_required_transfers ();
+    qs = TALER_ARL_adb->insert_wire_out_inconsistency (
+      TALER_ARL_adb->cls,
+      &woi);
+    if (qs < 0)
+    {
+      global_qs = qs;
+      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+      return GNUNET_SYSERR;
+    }
+    TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_minus),
+                          &TALER_ARL_USE_AB (total_bad_amount_out_minus),
+                          &roi->details.amount);
+    TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus),
+                          &TALER_ARL_USE_AB (total_bad_amount_out_plus),
+                          &amount);
+    return GNUNET_YES; /* justified, kind-of */
+  }
+  /* profit drain was correct */
+  TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_drained),
+                        &TALER_ARL_USE_AB (total_drained),
+                        &amount);
+  return GNUNET_YES;
 }
 
 
 /**
- * Check that @a want is within #TIME_TOLERANCE of @a have.
- * Otherwise report an inconsistency in row @a rowid of @a table.
+ * Check whether the given transfer was justified by a reserve closure or
+ * profit drain. If not, complain that we failed to match an entry from
+ * #out_map.  This means a wire transfer was made without proper
+ * justification.
  *
- * @param table where is the inconsistency (if any)
- * @param rowid what is the row
- * @param want what is the expected time
- * @param have what is the time we got
- * @return true on success, false to abort
- */
-static bool
-check_time_difference (const char *table,
-                       uint64_t rowid,
-                       struct GNUNET_TIME_Timestamp want,
-                       struct GNUNET_TIME_Timestamp have)
+ * @param cls a `struct WireAccount`
+ * @param key unused key
+ * @param value the `struct ReserveOutInfo` to report
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+complain_out_not_found (void *cls,
+                        const struct GNUNET_HashCode *key,
+                        void *value)
 {
-  struct GNUNET_TIME_Relative delta;
-  char *details;
+  // struct WireAccount *wa = cls;
+  struct ReserveOutInfo *roi = value;
+  struct GNUNET_HashCode rkey;
+  struct CheckMatchContext ctx = {
+    .roi = roi,
+    .found = false
+  };
+  enum GNUNET_GenericReturnValue ret;
 
-  if (GNUNET_TIME_timestamp_cmp (have, >, want))
-    delta = GNUNET_TIME_absolute_get_difference (want.abs_time,
-                                                 have.abs_time);
-  else
-    delta = GNUNET_TIME_absolute_get_difference (have.abs_time,
-                                                 want.abs_time);
-  if (GNUNET_TIME_relative_cmp (delta,
-                                <=,
-                                TIME_TOLERANCE))
-    return true;
+  (void) cls;
+  (void) key;
+  hash_rc (roi->details.credit_account_uri,
+           &roi->details.wtid,
+           &rkey);
+  GNUNET_CONTAINER_multihashmap_get_multiple (reserve_closures,
+                                              &rkey,
+                                              &check_rc_matches,
+                                              &ctx);
+  if (ctx.found)
+    return GNUNET_OK;
+  ret = check_profit_drain (roi);
+  if (GNUNET_NO != ret)
+    return ret;
 
-  GNUNET_asprintf (&details,
-                   "execution date mismatch (%s)",
-                   GNUNET_TIME_relative2s (delta,
-                                           true));
   {
-    struct TALER_AUDITORDB_RowMinorInconsistencies rmi = {
-      .row_id = rowid,
-      .diagnostic = details,
-      .row_table = (char *) table
+    struct TALER_AUDITORDB_WireOutInconsistency woi = {
+      .row_id = roi->details.serial_id,
+      .destination_account = (char *) roi->details.credit_account_uri,
+      .diagnostic = "missing justification for outgoing wire transfer",
+      .expected = zero,
+      .claimed = roi->details.amount
     };
     enum GNUNET_DB_QueryStatus qs;
 
-    qs = TALER_ARL_adb->insert_row_minor_inconsistencies (
+    qs = TALER_ARL_adb->insert_wire_out_inconsistency (
       TALER_ARL_adb->cls,
-      &rmi);
-
+      &woi);
     if (qs < 0)
     {
       global_qs = qs;
       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
-      GNUNET_free (details);
-      return false;
+      return GNUNET_SYSERR;
     }
   }
-  GNUNET_free (details);
-  return true;
+  TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus),
+                        &TALER_ARL_USE_AB (total_bad_amount_out_plus),
+                        &roi->details.amount);
+  return GNUNET_OK;
 }
 
 
@@ -1049,17 +956,17 @@ check_time_difference (const char *table,
  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
  */
 static enum GNUNET_GenericReturnValue
-wire_out_cb (void *cls,
-             uint64_t rowid,
-             struct GNUNET_TIME_Timestamp date,
-             const struct TALER_WireTransferIdentifierRawP *wtid,
-             const char *payto_uri,
-             const struct TALER_Amount *amount)
+wire_out_cb (
+  void *cls,
+  uint64_t rowid,
+  struct GNUNET_TIME_Timestamp date,
+  const struct TALER_WireTransferIdentifierRawP *wtid,
+  const char *payto_uri,
+  const struct TALER_Amount *amount)
 {
   struct WireAccount *wa = cls;
   struct GNUNET_HashCode key;
   struct ReserveOutInfo *roi;
-  enum GNUNET_GenericReturnValue ret = GNUNET_OK;
 
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Exchange wire OUT #%llu at %s of %s with WTID %s\n",
@@ -1084,7 +991,7 @@ wire_out_cb (void *cls,
     struct TALER_AUDITORDB_WireOutInconsistency woi = {
       .row_id = rowid,
       .destination_account = (char *) payto_uri,
-      .diagnostic = "expected wire transfer missing",
+      .diagnostic = "expected outgoing wire transfer missing",
       .expected = *amount,
       .claimed = zero,
     };
@@ -1140,9 +1047,9 @@ wire_out_cb (void *cls,
                              amount))
   {
     struct TALER_AUDITORDB_WireOutInconsistency woi = {
-      .row_id = rowid,
       .destination_account = (char *) payto_uri,
       .diagnostic = "wire amount does not match",
+      .wire_out_row_id = rowid,
       .expected = *amount,
       .claimed = zero,
     };
@@ -1185,268 +1092,28 @@ wire_out_cb (void *cls,
     return GNUNET_OK;
   }
 
-  if (! check_time_difference ("wire_out",
-                               rowid,
-                               date,
-                               roi->details.execution_date))
-    ret = GNUNET_SYSERR;
-  GNUNET_assert (GNUNET_OK ==
-                 free_roi (NULL,
-                           &key,
-                           roi));
-  wa->last_wire_out_serial_id = rowid + 1;
-  return ret;
-}
-
-
-/**
- * Closure for #check_rc_matches
- */
-struct CheckMatchContext
-{
-
-  /**
-   * Reserve operation looking for a match
-   */
-  const struct ReserveOutInfo *roi;
-
-  /**
-   * Set to true if we found a match.
-   */
-  bool found;
-};
-
-
-/**
- * Check if any of the reserve closures match the given wire transfer.
- *
- * @param[in,out] cls a `struct CheckMatchContext`
- * @param key key of @a value in #reserve_closures
- * @param value a `struct ReserveClosure`
- */
-static enum GNUNET_GenericReturnValue
-check_rc_matches (void *cls,
-                  const struct GNUNET_HashCode *key,
-                  void *value)
-{
-  struct CheckMatchContext *ctx = cls;
-  struct ReserveClosure *rc = value;
-
-  if ((0 == GNUNET_memcmp (&ctx->roi->details.wtid,
-                           &rc->wtid)) &&
-      (0 == strcasecmp (rc->receiver_account,
-                        ctx->roi->details.credit_account_uri)) &&
-      (0 == TALER_amount_cmp (&rc->amount,
-                              &ctx->roi->details.amount)))
   {
-    if (! check_time_difference ("reserves_closures",
-                                 rc->rowid,
-                                 rc->execution_date,
-                                 ctx->roi->details.execution_date))
-    {
-      free_rc (NULL,
-               key,
-               rc);
-      return GNUNET_SYSERR;
-    }
-    ctx->found = true;
-    free_rc (NULL,
-             key,
-             rc);
-    return GNUNET_NO;
-  }
-  return GNUNET_OK;
-}
-
+    enum GNUNET_GenericReturnValue ret;
 
-/**
- * Check whether the given transfer was justified by a reserve closure or
- * profit drain. If not, complain that we failed to match an entry from
- * #out_map.  This means a wire transfer was made without proper
- * justification.
- *
- * @param cls a `struct WireAccount`
- * @param key unused key
- * @param value the `struct ReserveOutInfo` to report
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-complain_out_not_found (void *cls,
-                        const struct GNUNET_HashCode *key,
-                        void *value)
-{
-  // struct WireAccount *wa = cls;
-  struct ReserveOutInfo *roi = value;
-  struct GNUNET_HashCode rkey;
-  struct CheckMatchContext ctx = {
-    .roi = roi,
-    .found = false
-  };
-
-  (void) key;
-  hash_rc (roi->details.credit_account_uri,
-           &roi->details.wtid,
-           &rkey);
-  GNUNET_CONTAINER_multihashmap_get_multiple (reserve_closures,
-                                              &rkey,
-                                              &check_rc_matches,
-                                              &ctx);
-  if (ctx.found)
-    return GNUNET_OK;
-  /* check for profit drain */
-  {
-    enum GNUNET_DB_QueryStatus qs;
-    uint64_t serial;
-    char *account_section;
-    char *payto_uri;
-    struct GNUNET_TIME_Timestamp request_timestamp;
-    struct TALER_Amount amount;
-    struct TALER_MasterSignatureP master_sig;
-
-    qs = TALER_ARL_edb->get_drain_profit (TALER_ARL_edb->cls,
-                                          &roi->details.wtid,
-                                          &serial,
-                                          &account_section,
-                                          &payto_uri,
-                                          &request_timestamp,
-                                          &amount,
-                                          &master_sig);
-    switch (qs)
+    if (! check_time_difference ("wire_out",
+                                 rowid,
+                                 date,
+                                 roi->details.execution_date))
     {
-    case GNUNET_DB_STATUS_HARD_ERROR:
-      GNUNET_break (0);
-      global_ret = EXIT_FAILURE;
-      GNUNET_SCHEDULER_shutdown ();
-      return GNUNET_SYSERR;
-    case GNUNET_DB_STATUS_SOFT_ERROR:
-      /* should fail on commit later ... */
-      GNUNET_break (0);
-      return GNUNET_NO;
-    case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
-      /* not a profit drain */
-      break;
-    case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "Profit drain of %s to %s found!\n",
-                  TALER_amount2s (&amount),
-                  payto_uri);
-      if (GNUNET_OK !=
-          TALER_exchange_offline_profit_drain_verify (
-            &roi->details.wtid,
-            request_timestamp,
-            &amount,
-            account_section,
-            payto_uri,
-            &TALER_ARL_master_pub,
-            &master_sig))
-      {
-        struct TALER_AUDITORDB_RowInconsistency ri = {
-          .row_id = roi->details.serial_id,
-          .row_table = "profit_drains",
-          .diagnostic = "invalid signature"
-        };
-
-        GNUNET_break (0);
-        qs = TALER_ARL_adb->insert_row_inconsistency (
-          TALER_ARL_adb->cls,
-          &ri);
-        if (qs < 0)
-        {
-          global_qs = qs;
-          GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
-          return GNUNET_SYSERR;
-        }
-        TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus),
-                              &TALER_ARL_USE_AB (total_bad_amount_out_plus),
-                              &amount);
-      }
-      else if (0 !=
-               strcasecmp (payto_uri,
-                           roi->details.credit_account_uri))
-      {
-        struct TALER_AUDITORDB_WireOutInconsistency woi = {
-          .row_id = serial,
-          .destination_account = (char *) roi->details.credit_account_uri,
-          .diagnostic = "amount wired to invalid account",
-          .expected = roi->details.amount,
-          .claimed = zero,
-        };
-
-        qs = TALER_ARL_adb->insert_wire_out_inconsistency (
-          TALER_ARL_adb->cls,
-          &woi);
-        if (qs < 0)
-        {
-          global_qs = qs;
-          GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
-          return GNUNET_SYSERR;
-        }
-        TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus),
-                              &TALER_ARL_USE_AB (total_bad_amount_out_plus),
-                              &amount);
-      }
-      else if (0 !=
-               TALER_amount_cmp (&amount,
-                                 &roi->details.amount))
-      {
-        struct TALER_AUDITORDB_WireOutInconsistency woi = {
-          .row_id = roi->details.serial_id,
-          .destination_account = (char *) roi->details.credit_account_uri,
-          .diagnostic = "incorrect amount to correct account",
-          .expected = roi->details.amount,
-          .claimed = amount,
-        };
-
-        qs = TALER_ARL_adb->insert_wire_out_inconsistency (
-          TALER_ARL_adb->cls,
-          &woi);
-        if (qs < 0)
-        {
-          global_qs = qs;
-          GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
-          return GNUNET_SYSERR;
-        }
-        TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_minus),
-                              &TALER_ARL_USE_AB (total_bad_amount_out_minus),
-                              &roi->details.amount);
-        TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus),
-                              &TALER_ARL_USE_AB (total_bad_amount_out_plus),
-                              &amount);
-      }
-      GNUNET_free (account_section);
-      GNUNET_free (payto_uri);
-      /* profit drain was correct */
-      TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_drained),
-                            &TALER_ARL_USE_AB (total_drained),
-                            &amount);
-      return GNUNET_OK;
+      /* We had a database error, fail */
+      ret = GNUNET_SYSERR;
     }
-  }
-
-  {
-    struct TALER_AUDITORDB_WireOutInconsistency woi = {
-      .row_id = roi->details.serial_id,
-      .destination_account = (char *) roi->details.credit_account_uri,
-      .diagnostic = "missing justification for outgoing wire transfer",
-      .expected = zero,
-      .claimed  =roi->details.amount
-    };
-    enum GNUNET_DB_QueryStatus qs;
-
-    qs = TALER_ARL_adb->insert_wire_out_inconsistency (
-      TALER_ARL_adb->cls,
-      &woi);
-    if (qs < 0)
+    else
     {
-      global_qs = qs;
-      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
-      return GNUNET_SYSERR;
+      ret = GNUNET_OK;
     }
+    GNUNET_assert (GNUNET_OK ==
+                   free_roi (NULL,
+                             &key,
+                             roi));
+    wa->last_wire_out_serial_id = rowid + 1;
+    return ret;
   }
-  TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus),
-                        &TALER_ARL_USE_AB (total_bad_amount_out_plus),
-                        &roi->details.amount);
-  return GNUNET_OK;
 }
 
 
@@ -1455,17 +1122,17 @@ complain_out_not_found (void *cls,
  * the DEBIT transactions this time, and then verify that all of them are
  * justified by 'reserves_out'.
  *
- * @param cls `struct WireAccount` with a wire account list to process
+ * @param[in,out] wa wire account list to process
  */
 static void
-process_debits (void *cls);
+process_debits (struct WireAccount *wa);
 
 
 /**
  * Go over the "wire_out" table of the exchange and
  * verify that all wire outs are in that table.
  *
- * @param wa wire account we are processing
+ * @param[in,out] wa wire account we are processing
  */
 static void
 check_exchange_wire_out (struct WireAccount *wa)
@@ -1509,8 +1176,9 @@ check_exchange_wire_out (struct WireAccount *wa)
  * @param dhr HTTP response details
  */
 static void
-history_debit_cb (void *cls,
-                  const struct TALER_BANK_DebitHistoryResponse *dhr)
+history_debit_cb (
+  void *cls,
+  const struct TALER_BANK_DebitHistoryResponse *dhr)
 {
   struct WireAccount *wa = cls;
   struct ReserveOutInfo *roi;
@@ -1524,23 +1192,22 @@ history_debit_cb (void *cls,
     {
       const struct TALER_BANK_DebitDetails *dd
         = &dhr->details.ok.details[i];
+
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                   "Analyzing bank DEBIT at %s of %s with WTID %s\n",
                   GNUNET_TIME_timestamp2s (dd->execution_date),
                   TALER_amount2s (&dd->amount),
                   TALER_B2S (&dd->wtid));
-      /* Update offset */
       wa->wire_off_out = dd->serial_id;
       slen = strlen (dd->credit_account_uri) + 1;
-      roi = GNUNET_malloc (sizeof (struct ReserveOutInfo)
-                           + slen);
       GNUNET_CRYPTO_hash (&dd->wtid,
                           sizeof (dd->wtid),
                           &roi->subject_hash);
-      roi->details.amount = dd->amount;
-      roi->details.execution_date = dd->execution_date;
-      roi->details.wtid = dd->wtid;
-      roi->details.credit_account_uri = (const char *) &roi[1];
+      roi = GNUNET_malloc (sizeof (struct ReserveOutInfo)
+                           + slen);
+      roi->details = *dd;
+      roi->details.credit_account_uri
+        = (const char *) &roi[1];
       GNUNET_memcpy (&roi[1],
                      dd->credit_account_uri,
                      slen);
@@ -1551,17 +1218,15 @@ history_debit_cb (void *cls,
                                              
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
       {
         struct TALER_AUDITORDB_WireFormatInconsistency wfi = {
-          // fixme: rowid!
-          .diagnostic = "duplicate subject hash",
           .amount = dd->amount,
-          .wire_offset = dd->serial_id
+          .wire_offset = dd->serial_id,
+          .diagnostic = "duplicate outgoing wire transfer subject"
         };
         enum GNUNET_DB_QueryStatus qs;
 
         qs = TALER_ARL_adb->insert_wire_format_inconsistency (
           TALER_ARL_adb->cls,
           &wfi);
-
         if (qs < 0)
         {
           global_qs = qs;
@@ -1569,8 +1234,10 @@ history_debit_cb (void *cls,
           commit (qs);
           return;
         }
-        TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_format_amount),
-                              &TALER_ARL_USE_AB (total_wire_format_amount),
+        TALER_ARL_amount_add (&TALER_ARL_USE_AB (
+                                wire_debit_duplicate_transfer_subject_total),
+                              &TALER_ARL_USE_AB (
+                                wire_debit_duplicate_transfer_subject_total),
                               &dd->amount);
       }
     }
@@ -1600,42 +1267,31 @@ history_debit_cb (void *cls,
 }
 
 
-/**
- * Main function for processing 'reserves_out' data.  We start by going over
- * the DEBIT transactions this time, and then verify that all of them are
- * justified by 'reserves_out'.
- *
- * @param cls `struct WireAccount` with a wire account list to process
- */
 static void
-process_debits (void *cls)
+process_debits (struct WireAccount *wa)
 {
-  struct WireAccount *wa = cls;
-
   /* skip accounts where DEBIT is not enabled */
   while ( (NULL != wa) &&
-          (GNUNET_NO == wa->ai->debit_enabled))
+          (! wa->ai->debit_enabled) )
     wa = wa->next;
   if (NULL == wa)
   {
-    /* end of iteration, now check wire_out to see
-       if it matches #out_map */
-    conclude_wire_out ();
+    /* end of iteration */
+    commit (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
     return;
   }
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Checking bank DEBIT records of account `%s'\n",
               wa->ai->section_name);
   GNUNET_assert (NULL == wa->dhh);
-  // FIXME: handle the case where more than INT32_MAX transactions exist.
-  // (CG: used to be INT64_MAX, changed by MS to INT32_MAX, why? To be 
discussed with him!)
-  wa->dhh = TALER_BANK_debit_history (ctx,
-                                      wa->ai->auth,
-                                      wa->wire_off_out,
-                                      INT32_MAX,
-                                      GNUNET_TIME_UNIT_ZERO,
-                                      &history_debit_cb,
-                                      wa);
+  wa->dhh = TALER_BANK_debit_history (
+    ctx,
+    wa->ai->auth,
+    wa->wire_off_out,
+    MAX_PER_TRANSACTION,
+    GNUNET_TIME_UNIT_ZERO,
+    &history_debit_cb,
+    wa);
   if (NULL == wa->dhh)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -1649,25 +1305,10 @@ process_debits (void *cls)
 }
 
 
-/**
- * Begin analyzing wire_out.
- */
-static void
-begin_debit_audit (void)
-{
-  GNUNET_assert (NULL == out_map);
-  out_map = GNUNET_CONTAINER_multihashmap_create (1024,
-                                                  true);
-  process_debits (wa_head);
-}
-
-
-/* ***************************** Setup logic ************************ */
-
 /**
  * Function called about reserve closing operations the aggregator triggered.
  *
- * @param cls closure
+ * @param cls closure; NULL
  * @param rowid row identifier used to uniquely identify the reserve closing 
operation
  * @param execution_date when did we execute the close operation
  * @param amount_with_fee how much did we debit the reserve
@@ -1680,15 +1321,16 @@ begin_debit_audit (void)
  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
  */
 static enum GNUNET_GenericReturnValue
-reserve_closed_cb (void *cls,
-                   uint64_t rowid,
-                   struct GNUNET_TIME_Timestamp execution_date,
-                   const struct TALER_Amount *amount_with_fee,
-                   const struct TALER_Amount *closing_fee,
-                   const struct TALER_ReservePublicKeyP *reserve_pub,
-                   const char *receiver_account,
-                   const struct TALER_WireTransferIdentifierRawP *wtid,
-                   uint64_t close_request_row)
+reserve_closed_cb (
+  void *cls,
+  uint64_t rowid,
+  struct GNUNET_TIME_Timestamp execution_date,
+  const struct TALER_Amount *amount_with_fee,
+  const struct TALER_Amount *closing_fee,
+  const struct TALER_ReservePublicKeyP *reserve_pub,
+  const char *receiver_account,
+  const struct TALER_WireTransferIdentifierRawP *wtid,
+  uint64_t close_request_row)
 {
   struct ReserveClosure *rc;
   struct GNUNET_HashCode key;
@@ -1702,8 +1344,9 @@ reserve_closed_cb (void *cls,
                                      closing_fee))
   {
     struct TALER_AUDITORDB_RowInconsistency ri = {
+      .row_id = rowid,
       .row_table = "reserves_closures",
-      .diagnostic = "closing fee above total amount"
+      .diagnostic = "closing fee above reserve balance (and closed anyway)"
     };
     enum GNUNET_DB_QueryStatus qs;
 
@@ -1729,10 +1372,11 @@ reserve_closed_cb (void *cls,
   hash_rc (receiver_account,
            wtid,
            &key);
-  (void) GNUNET_CONTAINER_multihashmap_put (reserve_closures,
-                                            &key,
-                                            rc,
-                                            
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+  (void) GNUNET_CONTAINER_multihashmap_put (
+    reserve_closures,
+    &key,
+    rc,
+    GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
   return GNUNET_OK;
 }
 
@@ -1784,7 +1428,7 @@ begin_transaction (void)
     TALER_ARL_GET_AB (total_bad_amount_out_minus),
     TALER_ARL_GET_AB (total_amount_lag),
     TALER_ARL_GET_AB (total_closure_amount_lag),
-    TALER_ARL_GET_AB (total_wire_format_amount),
+    TALER_ARL_GET_AB (wire_debit_duplicate_transfer_subject_total),
     TALER_ARL_GET_AB (total_wire_out),
     NULL);
   switch (qs)
@@ -1828,8 +1472,6 @@ begin_transaction (void)
   qs = TALER_ARL_adb->get_auditor_progress (
     TALER_ARL_adb->cls,
     TALER_ARL_GET_PP (wire_reserve_close_id),
-    TALER_ARL_GET_PP (wire_batch_deposit_id),
-    TALER_ARL_GET_PP (wire_aggregation_id),
     NULL);
   if (0 > qs)
   {
@@ -1844,10 +1486,8 @@ begin_transaction (void)
   else
   {
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Resuming wire audit at %llu / %llu / %llu\n",
-                (unsigned long long) TALER_ARL_USE_PP (wire_reserve_close_id),
-                (unsigned long long) TALER_ARL_USE_PP (wire_batch_deposit_id),
-                (unsigned long long) TALER_ARL_USE_PP (wire_aggregation_id));
+                "Resuming wire debit audit at %llu\n",
+                (unsigned long long) TALER_ARL_USE_PP (wire_reserve_close_id));
   }
 
   qs = TALER_ARL_edb->select_reserve_closed_above_serial_id (
@@ -1860,7 +1500,7 @@ begin_transaction (void)
     GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
     return GNUNET_DB_STATUS_HARD_ERROR;
   }
-  begin_debit_audit ();
+  process_debits (wa_head);
   return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
 }
 
@@ -1880,8 +1520,8 @@ process_account_cb (void *cls,
   struct WireAccount *wa;
 
   (void) cls;
-  if ((! ai->debit_enabled) &&
-      (! ai->credit_enabled))
+  if ( (! ai->debit_enabled) &&
+       (! ai->credit_enabled) )
     return; /* not an active exchange account */
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Found exchange account `%s'\n",
@@ -1980,6 +1620,8 @@ run (void *cls,
   }
   reserve_closures = GNUNET_CONTAINER_multihashmap_create (1024,
                                                            GNUNET_NO);
+  out_map = GNUNET_CONTAINER_multihashmap_create (1024,
+                                                  true);
   if (GNUNET_OK !=
       TALER_EXCHANGEDB_load_accounts (TALER_ARL_cfg,
                                       TALER_EXCHANGEDB_ALO_DEBIT
diff --git a/src/auditordb/0002-auditor_wire_out_inconsistency.sql 
b/src/auditordb/0002-auditor_wire_out_inconsistency.sql
index 484fd2ac6..d46c2b283 100644
--- a/src/auditordb/0002-auditor_wire_out_inconsistency.sql
+++ b/src/auditordb/0002-auditor_wire_out_inconsistency.sql
@@ -14,13 +14,14 @@
 -- TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 --
 
-SET search_path TO auditor;
 CREATE TABLE IF NOT EXISTS auditor_wire_out_inconsistency
 (
     row_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE PRIMARY KEY,
     destination_account TEXT NOT NULL,
-    expected taler_amount,
-    claimed taler_amount,
+    diagnostic TEXT NOT NULL,
+    wire_out_serial_id INT8 NOT NULL,
+    expected taler_amount NOT NULL,
+    claimed taler_amount NOT NULL,
     suppressed BOOLEAN NOT NULL DEFAULT FALSE
 );
 COMMENT ON TABLE auditor_wire_out_inconsistency
diff --git a/src/auditordb/pg_get_wire_format_inconsistency.c 
b/src/auditordb/pg_get_wire_format_inconsistency.c
index d94c851c8..333f9c9c0 100644
--- a/src/auditordb/pg_get_wire_format_inconsistency.c
+++ b/src/auditordb/pg_get_wire_format_inconsistency.c
@@ -13,14 +13,11 @@
    You should have received a copy of the GNU General Public License along with
    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
-
-
 #include "platform.h"
 #include "taler_error_codes.h"
 #include "taler_dbevents.h"
 #include "taler_pq_lib.h"
 #include "pg_helper.h"
-
 #include "pg_get_wire_format_inconsistency.h"
 
 
@@ -69,17 +66,18 @@ wire_format_inconsistency_cb (void *cls,
   for (unsigned int i = 0; i < num_results; i++)
   {
     uint64_t serial_id;
-
     struct TALER_AUDITORDB_WireFormatInconsistency dc;
-
     struct GNUNET_PQ_ResultSpec rs[] = {
-
-      GNUNET_PQ_result_spec_uint64 ("row_id", &serial_id),
-
-      TALER_PQ_RESULT_SPEC_AMOUNT ("amount",  &dc.amount),
-      GNUNET_PQ_result_spec_int64 ("wire_offset",  &dc.wire_offset),
-      GNUNET_PQ_result_spec_auto_from_type ("diagnostic",  &dc.diagnostic),
-      GNUNET_PQ_result_spec_bool ("suppressed",  &dc.suppressed),
+      GNUNET_PQ_result_spec_uint64 ("row_id",
+                                    &serial_id),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
+                                   &dc.amount),
+      GNUNET_PQ_result_spec_uint64 ("wire_offset",
+                                    &dc.wire_offset),
+      GNUNET_PQ_result_spec_auto_from_type ("diagnostic",
+                                            &dc.diagnostic),
+      GNUNET_PQ_result_spec_bool ("suppressed",
+                                  &dc.suppressed),
 
 
       GNUNET_PQ_result_spec_end
@@ -95,9 +93,7 @@ wire_format_inconsistency_cb (void *cls,
       dcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
       return;
     }
-
     dcc->qs = i + 1;
-
     rval = dcc->cb (dcc->cb_cls,
                     serial_id,
                     &dc);
@@ -113,14 +109,12 @@ TAH_PG_get_wire_format_inconsistency (
   void *cls,
   int64_t limit,
   uint64_t offset,
-  bool return_suppressed,             // maybe not needed
+  bool return_suppressed,
   TALER_AUDITORDB_WireFormatInconsistencyCallback cb,
   void *cb_cls)
 {
-
-  uint64_t plimit = (uint64_t) ((limit < 0) ? -limit : limit);
-
   struct PostgresClosure *pg = cls;
+  uint64_t plimit = (uint64_t) ((limit < 0) ? -limit : limit);
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_uint64 (&offset),
     GNUNET_PQ_query_param_bool (return_suppressed),
@@ -144,7 +138,7 @@ TAH_PG_get_wire_format_inconsistency (
            " suppressed"
            " FROM auditor_wire_format_inconsistency"
            " WHERE (row_id < $1)"
-           " AND ($2 OR suppressed is false)"
+           "   AND ($2 OR NOT suppressed)"
            " ORDER BY row_id DESC"
            " LIMIT $3"
            );
@@ -158,20 +152,18 @@ TAH_PG_get_wire_format_inconsistency (
            " suppressed"
            " FROM auditor_wire_format_inconsistency"
            " WHERE (row_id > $1)"
-           " AND ($2 OR suppressed is false)"
+           "   AND ($2 OR NOT suppressed)"
            " ORDER BY row_id ASC"
            " LIMIT $3"
            );
-  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
-                                             (limit > 0)
-                                             ?
-                                             
"auditor_wire_format_inconsistency_get_asc"
-                                             :
-                                             
"auditor_wire_format_inconsistency_get_desc",
-                                             params,
-                                             &wire_format_inconsistency_cb,
-                                             &dcc);
-
+  qs = GNUNET_PQ_eval_prepared_multi_select (
+    pg->conn,
+    (limit > 0)
+    ? "auditor_wire_format_inconsistency_get_asc"
+    : "auditor_wire_format_inconsistency_get_desc",
+    params,
+    &wire_format_inconsistency_cb,
+    &dcc);
   if (qs > 0)
     return dcc.qs;
   GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
diff --git a/src/auditordb/pg_get_wire_out_inconsistency.c 
b/src/auditordb/pg_get_wire_out_inconsistency.c
index d885a2fa1..b875f6051 100644
--- a/src/auditordb/pg_get_wire_out_inconsistency.c
+++ b/src/auditordb/pg_get_wire_out_inconsistency.c
@@ -68,21 +68,22 @@ wire_out_inconsistency_cb (void *cls,
 
   for (unsigned int i = 0; i < num_results; i++)
   {
-    uint64_t serial_id;
-
     struct TALER_AUDITORDB_WireOutInconsistency dc;
-
     struct GNUNET_PQ_ResultSpec rs[] = {
-
-      GNUNET_PQ_result_spec_uint64 ("row_id", &serial_id),
-
+      GNUNET_PQ_result_spec_uint64 ("row_id",
+                                    &dc.row_id),
       GNUNET_PQ_result_spec_string ("destination_account",
                                     &dc.destination_account),
-      TALER_PQ_RESULT_SPEC_AMOUNT ("expected",  &dc.expected),
-      TALER_PQ_RESULT_SPEC_AMOUNT ("claimed",  &dc.claimed),
-      GNUNET_PQ_result_spec_bool ("suppressed",  &dc.suppressed),
-
-
+      GNUNET_PQ_result_spec_string ("diagnostic",
+                                    &dc.diagnostic),
+      GNUNET_PQ_result_spec_uint64 ("wire_out_row_id",
+                                    &dc.wire_out_row_id),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("expected",
+                                   &dc.expected),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("claimed",
+                                   &dc.claimed),
+      GNUNET_PQ_result_spec_bool ("suppressed",
+                                  &dc.suppressed),
       GNUNET_PQ_result_spec_end
     };
     enum GNUNET_GenericReturnValue rval;
@@ -96,11 +97,8 @@ wire_out_inconsistency_cb (void *cls,
       dcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
       return;
     }
-
     dcc->qs = i + 1;
-
     rval = dcc->cb (dcc->cb_cls,
-                    serial_id,
                     &dc);
     GNUNET_PQ_cleanup_result (rs);
     if (GNUNET_OK != rval)
@@ -114,14 +112,12 @@ TAH_PG_get_wire_out_inconsistency (
   void *cls,
   int64_t limit,
   uint64_t offset,
-  bool return_suppressed,             // maybe not needed
+  bool return_suppressed,
   TALER_AUDITORDB_WireOutInconsistencyCallback cb,
   void *cb_cls)
 {
-
-  uint64_t plimit = (uint64_t) ((limit < 0) ? -limit : limit);
-
   struct PostgresClosure *pg = cls;
+  uint64_t plimit = (uint64_t) ((limit < 0) ? -limit : limit);
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_uint64 (&offset),
     GNUNET_PQ_query_param_bool (return_suppressed),
@@ -138,41 +134,43 @@ TAH_PG_get_wire_out_inconsistency (
   PREPARE (pg,
            "auditor_wire_out_inconsistency_get_desc",
            "SELECT"
-           " row_id,"
-           " destination_account,"
-           " expected,"
-           " claimed,"
-           " suppressed"
+           " row_id"
+           ",destination_account"
+           ",diagnostic"
+           ",wire_out_row_id"
+           ",expected"
+           ",claimed"
+           ",suppressed"
            " FROM auditor_wire_out_inconsistency"
            " WHERE (row_id < $1)"
-           " AND ($2 OR suppressed is false)"
+           "   AND ($2 OR NOT suppressed)"
            " ORDER BY row_id DESC"
            " LIMIT $3"
            );
   PREPARE (pg,
            "auditor_wire_out_inconsistency_get_asc",
            "SELECT"
-           " row_id,"
-           " destination_account,"
-           " expected,"
-           " claimed,"
-           " suppressed"
+           " row_id"
+           ",destination_account"
+           ",diagnostic"
+           ",wire_out_row_id"
+           ",expected"
+           ",claimed"
+           ",suppressed"
            " FROM auditor_wire_out_inconsistency"
            " WHERE (row_id > $1)"
-           " AND ($2 OR suppressed is false)"
+           "   AND ($2 OR NOT suppressed)"
            " ORDER BY row_id ASC"
            " LIMIT $3"
            );
-  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
-                                             (limit > 0)
-                                             ?
-                                             
"auditor_wire_out_inconsistency_get_asc"
-                                             :
-                                             
"auditor_wire_out_inconsistency_get_desc",
-                                             params,
-                                             &wire_out_inconsistency_cb,
-                                             &dcc);
-
+  qs = GNUNET_PQ_eval_prepared_multi_select (
+    pg->conn,
+    (limit > 0)
+    ? "auditor_wire_out_inconsistency_get_asc"
+    : "auditor_wire_out_inconsistency_get_desc",
+    params,
+    &wire_out_inconsistency_cb,
+    &dcc);
   if (qs > 0)
     return dcc.qs;
   GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
diff --git a/src/auditordb/pg_insert_wire_format_inconsistency.c 
b/src/auditordb/pg_insert_wire_format_inconsistency.c
index c4b5b16dc..73fe677b4 100644
--- a/src/auditordb/pg_insert_wire_format_inconsistency.c
+++ b/src/auditordb/pg_insert_wire_format_inconsistency.c
@@ -13,14 +13,12 @@
    You should have received a copy of the GNU General Public License along with
    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
-
-
 #include "platform.h"
 #include "taler_pq_lib.h"
 #include "pg_helper.h"
-
 #include "pg_insert_wire_format_inconsistency.h"
 
+
 enum GNUNET_DB_QueryStatus
 TAH_PG_insert_wire_format_inconsistency (
   void *cls,
@@ -30,17 +28,15 @@ TAH_PG_insert_wire_format_inconsistency (
   struct GNUNET_PQ_QueryParam params[] = {
 
     TALER_PQ_query_param_amount (pg->conn, &dc->amount),
-    GNUNET_PQ_query_param_int64 (&dc->wire_offset),
+    GNUNET_PQ_query_param_uint64 (&dc->wire_offset),
     GNUNET_PQ_query_param_auto_from_type (&dc->diagnostic),
-
-
     GNUNET_PQ_query_param_end
   };
 
   PREPARE (pg,
            "auditor_wire_format_inconsistency_insert",
            "INSERT INTO auditor_wire_format_inconsistency "
-           "( row_id,"
+           "(row_id,"
            " amount,"
            " wire_offset,"
            " diagnostic"
diff --git a/src/auditordb/pg_insert_wire_out_inconsistency.c 
b/src/auditordb/pg_insert_wire_out_inconsistency.c
index 828aa3293..2e9354644 100644
--- a/src/auditordb/pg_insert_wire_out_inconsistency.c
+++ b/src/auditordb/pg_insert_wire_out_inconsistency.c
@@ -28,22 +28,25 @@ TAH_PG_insert_wire_out_inconsistency (
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
-
     GNUNET_PQ_query_param_string (dc->destination_account),
-    TALER_PQ_query_param_amount (pg->conn, &dc->expected),
-    TALER_PQ_query_param_amount (pg->conn, &dc->claimed),
-
-
+    GNUNET_PQ_query_param_string (dc->diagnostic),
+    GNUNET_PQ_query_param_uint64 (&dc->wire_out_row_id),
+    TALER_PQ_query_param_amount (pg->conn,
+                                 &dc->expected),
+    TALER_PQ_query_param_amount (pg->conn,
+                                 &dc->claimed),
     GNUNET_PQ_query_param_end
   };
 
   PREPARE (pg,
            "auditor_wire_out_inconsistency_insert",
            "INSERT INTO auditor_wire_out_inconsistency "
-           "( destination_account,"
-           " expected,"
-           " claimed"
-           ") VALUES ($1,$2,$3);"
+           "(destination_account"
+           ",diagnostic"
+           ",wire_out_serial_id"
+           ",expected"
+           ",claimed"
+           ") VALUES ($1,$2,$3,$4,$5);"
            );
   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
                                              
"auditor_wire_out_inconsistency_insert",
diff --git a/src/include/taler_auditordb_plugin.h 
b/src/include/taler_auditordb_plugin.h
index 430fad9d0..1dd26a8ce 100644
--- a/src/include/taler_auditordb_plugin.h
+++ b/src/include/taler_auditordb_plugin.h
@@ -781,7 +781,7 @@ struct TALER_AUDITORDB_WireFormatInconsistency
 {
   uint64_t row_id;
   struct TALER_Amount amount;
-  int64_t wire_offset;
+  uint64_t wire_offset;
   char *diagnostic;
   bool suppressed;
 
@@ -791,7 +791,8 @@ struct TALER_AUDITORDB_WireOutInconsistency
 {
   uint64_t row_id;
   char *destination_account;
-  char *diagnostic; // FIXME: new
+  char *diagnostic;
+  uint64_t wire_out_row_id;
   struct TALER_Amount expected;
   struct TALER_Amount claimed;
   bool suppressed;
@@ -951,7 +952,6 @@ typedef enum GNUNET_GenericReturnValue
 typedef enum GNUNET_GenericReturnValue
 (*TALER_AUDITORDB_WireOutInconsistencyCallback)(
   void *cls,
-  uint64_t serial_id,
   const struct TALER_AUDITORDB_WireOutInconsistency *dc);
 
 typedef enum GNUNET_GenericReturnValue

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