aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2024-08-23 14:15:18 +0200
committerChristian Grothoff <christian@grothoff.org>2024-08-23 14:16:03 +0200
commit9c21e22da9e2f411cfc3d2c900f4aabe2e3a88f1 (patch)
tree0a5bea2d721f5af7597d7930bf7a4b1fa7f042b3 /src
parenta70c64d085bddfae6ebc07ebd9ceb796fbf03e62 (diff)
split off taler-helper-auditor-transfer to reduce mixing up different things in helpers; also clean up its code
Diffstat (limited to 'src')
-rw-r--r--src/auditor/.gitignore1
-rw-r--r--src/auditor/Makefile.am15
-rw-r--r--src/auditor/taler-auditor-httpd_wire-out-inconsistency-get.c69
-rw-r--r--src/auditor/taler-helper-auditor-transfer.c567
-rw-r--r--src/auditor/taler-helper-auditor-wire-debit.c1082
-rw-r--r--src/auditordb/0002-auditor_wire_out_inconsistency.sql7
-rw-r--r--src/auditordb/pg_get_wire_format_inconsistency.c52
-rw-r--r--src/auditordb/pg_get_wire_out_inconsistency.c78
-rw-r--r--src/auditordb/pg_insert_wire_format_inconsistency.c10
-rw-r--r--src/auditordb/pg_insert_wire_out_inconsistency.c21
-rw-r--r--src/include/taler_auditordb_plugin.h6
11 files changed, 1057 insertions, 851 deletions
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>
@@ -45,6 +50,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
* created by imperfect clock synchronization and network delay.
@@ -54,6 +65,13 @@
/**
+ * 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
+ * @param cls a `struct WireAccount`
+ * @param key unused key
+ * @param value the `struct ReserveOutInfo` to report
+ * @return #GNUNET_OK on success
*/
-static bool
-check_time_difference (const char *table,
- uint64_t rowid,
- struct GNUNET_TIME_Timestamp want,
- struct GNUNET_TIME_Timestamp have)
+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;
-}
-
-
-/**
- * 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
- };
+ enum GNUNET_GenericReturnValue ret;
- (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,
@@ -1650,24 +1306,9 @@ 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