summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--contrib/auditor-report.tex.j2184
-rwxr-xr-xcontrib/render.py26
-rw-r--r--src/auditor/Makefile.am1
-rw-r--r--src/auditor/taler-wire-auditor.c470
5 files changed, 545 insertions, 140 deletions
diff --git a/.gitignore b/.gitignore
index 16c62347..5f227d1d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -88,3 +88,7 @@ contrib/taler-exchange.tag
doxygen-doc/
src/exchange-lib/test_exchange_api_keys_cherry_picking
src/auditor/taler-wire-auditor
+contrib/auditor-report.aux
+contrib/auditor-report.log
+contrib/auditor-report.tex
+contrib/auditor-report.pdf
diff --git a/contrib/auditor-report.tex.j2 b/contrib/auditor-report.tex.j2
index 11b78413..d852f90c 100644
--- a/contrib/auditor-report.tex.j2
+++ b/contrib/auditor-report.tex.j2
@@ -311,11 +311,130 @@ public key for ``payback-master'' operations.
\subsection{Actual incoming wire transfers}
-TBD. See bug 4958.
+This section highlights cases where the exchange's record about
+incoming wire transfers does not match with that of the bank.
+
+{% if wire.reserve_in_amount_inconsistencies() == 0 %}
+ {\bf All incoming wire transfer amounts and subjects matched up.}
+{% else %}
+ \begin{longtable}{p{6.5cm}|rl|rl}
+ {\bf Wire transfer identifier} & \multicolumn{2}{|c|}{ {\bf Wired}} & \multicolumn{2}{|c|}{ {\bf Expected}} \\
+ {\bf Diagnostic} & \multicolumn{2}{|c|}{ {\bf Row}} & \multicolumn{2}{|c|}{ {\bf Timestamp}} \\
+ \hline \hline
+\endfirsthead
+ {\bf Wire transfer identifier} & \multicolumn{2}{|c|}{ {\bf Wired}} & \multicolumn{2}{|c|}{ {\bf Expected}} \\
+ {\bf Diagnostic} & \multicolumn{2}{|c|}{ {\bf Row}} & \multicolumn{2}{|c|}{ {\bf Timestamp}} \\
+ \hline \hline
+\endhead
+ \hline \hline
+ {\bf Wire transfer identifier} & \multicolumn{2}{|c|}{ {\bf Wired}} & \multicolumn{2}{|c|}{ {\bf Expected}} \\
+ {\bf Diagnostic} & \multicolumn{2}{|c|}{ {\bf Row}} & \multicolumn{2}{|c|}{ {\bf Timestamp}} \\
+\endfoot
+ \hline
+ {\bf Total deltas} & &
+ {{ wire.total_bad_amount_in_plus.value}}.{{ wire.total_bad_amount_in_plus.fraction}} & {{ wire.total_bad_amount_in_plus.currency}} &
+ - {{ wire.total_bad_amount_in_minus.value}}.{{ wire.total_bad_amount_in_minus.fraction}} & {{ wire.total_bad_amount_in_minus.currency}} \\
+ \caption{Incoming wire transfer amounts not matching up.}
+ \label{table:wire_in:transfer_amount_inconsistencies}
+\endlastfoot
+{% for item in wire.reserve_in_amount_inconsistencies %}
+ {\tt {{ item.wtid }} } &
+ {{ item.amount_wired.value }}.{{ item.amount_wired.fraction }} &
+ {{ item.amount_wired.currency }} &
+ {{ item.amount_expected.value }}.{{ item.amount_expected.fraction }} &
+ {{ item.amount_expected.currency }} \\ \hline
+\nopagebreak
+ &
+ {{ item.diagnostic }} &
+ {{ item.row }} &
+ {{ item.timestmap }} \\ \hline
+{% endfor %}
+ \end{longtable}
+{% endif %}
+
+
+\subsection{Missattributed incoming wire transfers}
+
+This section lists cases where the sender account record of an
+incoming wire transfer differs between the exchange and the bank.
+This will cause funds to be sent to the wrong account when the reserve
+is closed and the remaining balance is refunded to the original
+account.
+
+
+{% if wire.missattribution_in_inconsistencies() == 0 %}
+ {\bf All incoming wire transfer sender accounts matched up.}
+{% else %}
+ \begin{longtable}{p{6.5cm}|rl}
+ {\bf Wire transfer identifier} & \multicolumn{2}{|c|}{ {\bf Amount}} \\
+ \hline \hline
+\endfirsthead
+ {\bf Wire transfer identifier} & \multicolumn{2}{|c|}{ {\bf Amount}} \\
+ \hline \hline
+\endhead
+ \hline \hline
+ {\bf Wire transfer identifier} & \multicolumn{2}{|c|}{ {\bf Amount}} \\
+\endfoot
+ \hline
+ {\bf Total amount} &
+ {{ wire.total_missattribution_in_plus.value}}.{{ wire.total_missattribution_in_plus.fraction}} & {{ wire.total_missattribution_in_plus.currency}} \\
+ \caption{Incoming wire transfer sender accounts not matching up.}
+ \label{table:wire_in:sender_account_inconsistencies}
+\endlastfoot
+{% for item in wire.missattribution_in_inconsistencies %}
+ {\tt {{ item.wtid }} } &
+ {{ item.amount.value }}.{{ item.amount.fraction }} &
+ {{ item.amount.currency }} \\ hline
+{% endfor %}
+ \end{longtable}
+{% endif %}
+
+
+
\subsection{Actual outgoing wire transfers} \label{sec:wire_check_out}
-TBD. See bug 4958.
+This section highlights cases where the exchange missbehaved
+with respect to outgoing wire transfers.
+
+{% if wire.wire_out_amount_inconsistencies() == 0 %}
+ {\bf All outgoing wire transfers matched up.}
+{% else %}
+ \begin{longtable}{p{6.5cm}|rl|rl}
+ {\bf Wire transfer identifier} & \multicolumn{2}{|c|}{ {\bf Wired}} & \multicolumn{2}{|c|}{ {\bf Justified}} \\
+ {\bf Diagnostic} & \multicolumn{2}{|c|}{ {\bf Row}} & \multicolumn{2}{|c|}{ {\bf Timestamp}} \\
+ \hline \hline
+\endfirsthead
+ {\bf Wire transfer identifier} & \multicolumn{2}{|c|}{ {\bf Wired}} & \multicolumn{2}{|c|}{ {\bf Justified}} \\
+ {\bf Diagnostic} & \multicolumn{2}{|c|}{ {\bf Row}} & \multicolumn{2}{|c|}{ {\bf Timestamp}} \\
+ \hline \hline
+\endhead
+ \hline \hline
+ {\bf Wire transfer identifier} & \multicolumn{2}{|c|}{ {\bf Wired}} & \multicolumn{2}{|c|}{ {\bf Justified}} \\
+ {\bf Diagnostic} & \multicolumn{2}{|c|}{ {\bf Row}} & \multicolumn{2}{|c|}{ {\bf Timestamp}} \\
+\endfoot
+ \hline
+ {\bf Total deltas} & &
+ {{ wire.total_bad_amount_out_plus.value}}.{{ wire.total_bad_amount_out_plus.fraction}} & {{ wire.total_bad_amount_out_plus.currency}} &
+ - {{ wire.total_bad_amount_out_minus.value}}.{{ wire.total_bad_amount_out_minus.fraction}} & {{ wire.total_bad_amount_out_minus.currency}} \\
+ \caption{Outgoing wire transfer amounts not matching up.}
+ \label{table:wire_out:transfer_amount_inconsistencies}
+\endlastfoot
+{% for item in wire.wire_out_amount_inconsistencies %}
+ {\tt {{ item.wtid }} } &
+ {{ item.amount_wired.value }}.{{ item.amount_wired.fraction }} &
+ {{ item.amount_wired.currency }} &
+ {{ item.amount_justified.value }}.{{ item.amount_justified.fraction }} &
+ {{ item.amount_justified.currency }} \\ \hline
+\nopagebreak
+ &
+ {{ item.diagnostic }} &
+ {{ item.row }} &
+ {{ item.timestmap }} \\ \hline
+{% endfor %}
+ \end{longtable}
+{% endif %}
+
\section{Minor irregularities}
@@ -358,6 +477,39 @@ translate into a financial loss (yet).
{% endif %}
+\subsection{Wire table issues}
+
+This section describes issues found by the wire auditor that do not
+have a clear financial impact.
+
+{% if wire.row_inconsistencies|length() == 0 %}
+ {\bf No wire row inconsistencies found.}
+{% else %}
+ \begin{longtable}{p{1.5cm}|l|p{5.5}}
+ {\bf Table} & {\bf Row} & {\bf Diagnostic} \\
+ \hline \hline
+\endfirsthead
+ {\bf Table} & {\bf Row} & {\bf Diagnostic} \\
+ \hline \hline
+\endhead
+ \hline \hline
+ {\bf Table} & {\bf Row} & {\bf Diagnostic} \\
+\endfoot
+ \hline
+ {\bf Table} & {\bf Row} & {\bf Diagnostic} \\
+ \caption{Other wire table issues found (by table and row).}
+ \label{table:misc}
+\endlastfoot
+{% for item in data.row_inconsistencies %}
+ {{ item.table }} &
+ {{ item.row }} &
+ {{ item.diagnostic }} \\
+\nopagebreak
+ {{ item.wire_offset_hash }} \\ \hline
+{% endfor %}
+ \end{longtable}
+{% endif %}
+
\subsection{Other issues}
This section describes issues found that do not have a clear financial
@@ -470,6 +622,34 @@ implications.
{% endif %}
+\subsection{Wire transfer timestamp issues}
+
+This section lists issues with wire transfers related to timestamps.
+
+
+{% if wire.row_minor_inconsistencies|length() == 0 %}
+ {\bf No timestamp issues detected.}
+{% else %}
+ \begin{longtable}{p{1.5cm}|r|p{5.5}}
+ {\bf Table} & {\bf Table row} & {\bf Diagnostic}
+ \\ \hline \hline
+\endfirsthead
+ {\bf Table} & {\bf Table row} & {\bf Diagnostic}
+ \\ \hline \hline
+\endhead
+ \hline \hline
+ {\bf Table} & {\bf Table row} & {\bf Diagnostic} \\
+\endfoot
+ \hline \hline
+ {\bf Table} & {\bf Table row} & {\bf Diagnostic} \\
+ \caption{Execution times not matching in wire transfers.}
+ \label{table:wire:bad_time}
+\endlastfoot
+{% for item in wire.row_minor_inconsistencies %}
+ {\tt {{ item.table }} } & {{ item.row }} & {{ item.diagnostic }} \\ \hline
+{% endfor %}
+ \end{longtable}
+{% endif %}
\end{document}
diff --git a/contrib/render.py b/contrib/render.py
index d31c7f9b..093db355 100755
--- a/contrib/render.py
+++ b/contrib/render.py
@@ -1,14 +1,19 @@
#!/usr/bin/python
# This file is in the public domain.
-"""
-Expand Jinja2 templates based on JSON input.
+"""Expand Jinja2 templates based on JSON input.
+
+First command-line argument must be the JSON input from taler-auditor.
+Second command-line argument must be the JSON input from the
+taler-wire-auditor.
+
+The tool then reads the template from stdin and writes the expanded
+output to stdout.
-First command-line argument must be the JSON input.
-The tool reads the template from stdin and writes
-the expanded output to stdout.
+TODO: proper installation, man page, error handling, --help option.
@author Christian Grothoff
+
"""
import sys
@@ -23,10 +28,13 @@ class StdinLoader(BaseLoader):
def get_source(self, environment, template):
source = sys.stdin.read().decode('utf-8')
return source, self.path, lambda: false
-
-jsonFile = open (sys.argv[1], 'r')
-jsonData = json.load(jsonFile)
+
+jsonFile1 = open (sys.argv[1], 'r')
+jsonData1 = json.load(jsonFile)
+
+jsonFile2 = open (sys.argv[2], 'r')
+jsonData2 = json.load(jsonFile)
jinjaEnv = jinja2.Environment(loader=StdinLoader(),
lstrip_blocks=True,
@@ -35,4 +43,4 @@ jinjaEnv = jinja2.Environment(loader=StdinLoader(),
autoescape=False)
tmpl = jinjaEnv.get_template('stdin');
-print(tmpl.render(data = jsonData))
+print(tmpl.render(data = jsonData1, wire = jsonData2))
diff --git a/src/auditor/Makefile.am b/src/auditor/Makefile.am
index 439db7f8..0770338a 100644
--- a/src/auditor/Makefile.am
+++ b/src/auditor/Makefile.am
@@ -39,6 +39,7 @@ taler_wire_auditor_LDADD = \
$(top_builddir)/src/exchangedb/libtalerexchangedb.la \
$(top_builddir)/src/auditordb/libtalerauditordb.la \
-ljansson \
+ -lgnunetjson \
-lgnunetutil
taler_auditor_sign_SOURCES = \
diff --git a/src/auditor/taler-wire-auditor.c b/src/auditor/taler-wire-auditor.c
index 7cc741b9..c61d2a38 100644
--- a/src/auditor/taler-wire-auditor.c
+++ b/src/auditor/taler-wire-auditor.c
@@ -130,7 +130,23 @@ static void *out_wire_off;
static size_t wire_off_size;
/**
- * Array of reports about row inconsitencies.
+ * Array of reports about row inconsitencies in wire_out table.
+ */
+static json_t *report_wire_out_inconsistencies;
+
+/**
+ * Array of reports about row inconsitencies in reserves_in table.
+ */
+static json_t *report_reserve_in_inconsistencies;
+
+/**
+ * Array of reports about wrong bank account being recorded for
+ * incoming wire transfers.
+ */
+static json_t *report_missattribution_in_inconsistencies;
+
+/**
+ * Array of reports about row inconcistencies.
*/
static json_t *report_row_inconsistencies;
@@ -139,17 +155,49 @@ static json_t *report_row_inconsistencies;
*/
static json_t *report_row_minor_inconsistencies;
+/**
+ * Total amount that was transferred too much from the exchange.
+ */
+static struct TALER_Amount total_bad_amount_out_plus;
+
+/**
+ * Total amount that was transferred too little from the exchange.
+ */
+static struct TALER_Amount total_bad_amount_out_minus;
+
+/**
+ * Total amount that was transferred too much to the exchange.
+ */
+static struct TALER_Amount total_bad_amount_in_plus;
+
+/**
+ * Total amount that was transferred too little to the exchange.
+ */
+static struct TALER_Amount total_bad_amount_in_minus;
+
+/**
+ * Total amount where the exchange has the wrong sender account
+ * for incoming funds and may thus wire funds to the wrong
+ * destination when closing the reserve.
+ */
+static struct TALER_Amount total_missattribution_in;
+
+/**
+ * Amount of zero in our currency.
+ */
+static struct TALER_Amount zero;
+
/* ***************************** Shutdown **************************** */
-/**
+/**
* Entry in map with wire information we expect to obtain from the
* bank later.
*/
struct ReserveInInfo
{
- /**
+ /**
* Hash of expected row offset.
*/
struct GNUNET_HashCode row_off_hash;
@@ -168,18 +216,18 @@ struct ReserveInInfo
* RowID in reserves_in table.
*/
uint64_t rowid;
-
+
};
-/**
+/**
* Entry in map with wire information we expect to obtain from the
* #edb later.
*/
struct ReserveOutInfo
{
- /**
+ /**
* Hash of the wire transfer subject.
*/
struct GNUNET_HashCode subject_hash;
@@ -188,7 +236,7 @@ struct ReserveOutInfo
* Expected details about the wire transfer.
*/
struct TALER_WIRE_TransferDetails details;
-
+
};
@@ -253,11 +301,32 @@ do_shutdown (void *cls)
if (NULL != report_row_inconsistencies)
{
json_t *report;
-
+
GNUNET_assert (NULL != report_row_minor_inconsistencies);
- report = json_pack ("{s:o, s:o}",
- "row-inconsistencies", report_row_inconsistencies,
- "row-minor-inconsistencies", report_row_minor_inconsistencies);
+ report = json_pack ("{s:o, s:o, s:o, s:o, s:o,"
+ " s:o, s:o, s:o, s:o, s:o }",
+ /* blocks of 5 */
+ "wire_out_amount_inconsistencies",
+ report_wire_out_inconsistencies,
+ "total_wire_out_delta_plus",
+ TALER_JSON_from_amount (&total_bad_amount_out_plus),
+ "total_wire_out_delta_minus",
+ TALER_JSON_from_amount (&total_bad_amount_out_minus),
+ "reserve_in_amount_inconsistencies",
+ report_reserve_in_inconsistencies,
+ "total_wire_in_delta_minus",
+ TALER_JSON_from_amount (&total_bad_amount_in_plus),
+ /* block */
+ "total_wire_in_delta_minus",
+ TALER_JSON_from_amount (&total_bad_amount_in_minus),
+ "missattribution_in_inconsistencies",
+ report_missattribution_in_inconsistencies,
+ "total_missattribution_in",
+ TALER_JSON_from_amount (&total_missattribution_in),
+ "row_inconsistencies",
+ report_row_inconsistencies,
+ "row_minor_inconsistencies",
+ report_row_minor_inconsistencies);
json_dumpf (report,
stdout,
JSON_INDENT (2));
@@ -313,7 +382,7 @@ do_shutdown (void *cls)
*
* @param array report array to append @a object to
* @param object object to append, should be check that it is not NULL
- */
+ */
static void
report (json_t *array,
json_t *object)
@@ -325,47 +394,6 @@ report (json_t *array,
}
-/**
- * Report a (serious) inconsistency in the exchange's database.
- *
- * @param table affected table
- * @param rowid affected row, UINT64_MAX if row is missing
- * @param diagnostic message explaining the problem
- */
-static void
-report_row_inconsistency (const char *table,
- uint64_t rowid,
- const char *diagnostic)
-{
- report (report_row_inconsistencies,
- json_pack ("{s:s, s:I, s:s}",
- "table", table,
- "row", (json_int_t) rowid,
- "diagnostic", diagnostic));
-}
-
-
-/**
- * Report a minor inconsistency in the exchange's database (i.e. something
- * relating to timestamps that should have no financial implications).
- *
- * @param table affected table
- * @param rowid affected row, UINT64_MAX if row is missing
- * @param diagnostic message explaining the problem
- */
-static void
-report_row_minor_inconsistency (const char *table,
- uint64_t rowid,
- const char *diagnostic)
-{
- report (report_row_minor_inconsistencies,
- json_pack ("{s:s, s:I, s:s}",
- "table", table,
- "row", (json_int_t) rowid,
- "diagnostic", diagnostic));
-}
-
-
/* *************************** General transaction logic ****************** */
/**
@@ -475,15 +503,15 @@ commit (enum GNUNET_DB_QueryStatus qs)
*/
static int
wire_out_cb (void *cls,
- uint64_t rowid,
- struct GNUNET_TIME_Absolute date,
- const struct TALER_WireTransferIdentifierRawP *wtid,
- const json_t *wire,
- const struct TALER_Amount *amount)
+ uint64_t rowid,
+ struct GNUNET_TIME_Absolute date,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ const json_t *wire,
+ const struct TALER_Amount *amount)
{
struct GNUNET_HashCode key;
struct ReserveOutInfo *roi;
-
+
GNUNET_CRYPTO_hash (wtid,
sizeof (struct TALER_WireTransferIdentifierRawP),
&key);
@@ -491,37 +519,108 @@ wire_out_cb (void *cls,
&key);
if (NULL == roi)
{
- /* FIXME (#4963): do proper logging! */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to find wire transfer `%s' over %s at `%s' in exchange database!\n",
- TALER_B2S (wtid),
- TALER_amount2s (amount),
- GNUNET_STRINGS_absolute_time_to_string (date));
+ /* Wire transfer was not made (yet) at all (but would have been
+ justified), so the entire amount is missing / still to be done.
+ This is moderately harmless, it might just be that the aggreator
+ has not yet fully caught up with the transfers it should do. */
+ report (report_wire_out_inconsistencies,
+ json_pack ("{s:I, s:o, s:o, s:o, s:s, s:s}",
+ "row", (json_int_t) rowid,
+ "amount_wired", TALER_JSON_from_amount (&zero),
+ "amount_justified", TALER_JSON_from_amount (amount),
+ "wtid", GNUNET_JSON_from_data_auto (wtid),
+ "timestamp", GNUNET_STRINGS_absolute_time_to_string (date),
+ "diagnostic", "wire transfer not made (yet?)"));
+ GNUNET_break (GNUNET_OK ==
+ TALER_amount_add (&total_bad_amount_out_minus,
+ &total_bad_amount_out_minus,
+ amount));
return GNUNET_OK;
}
+ if (! json_equal ((json_t *) wire,
+ roi->details.account_details))
+ {
+ /* Destination bank account is wrong in actual wire transfer, so
+ we should count the wire transfer as entirely spurious, and
+ additionally consider the justified wire transfer as missing. */
+ report (report_wire_out_inconsistencies,
+ json_pack ("{s:I, s:o, s:o, s:o, s:s, s:s}",
+ "row", (json_int_t) rowid,
+ "amount_wired", TALER_JSON_from_amount (&roi->details.amount),
+ "amount_justified", TALER_JSON_from_amount (&zero),
+ "wtid", GNUNET_JSON_from_data_auto (wtid),
+ "timestamp", GNUNET_STRINGS_absolute_time_to_string (date),
+ "diagnostic", "recevier account missmatch"));
+ GNUNET_break (GNUNET_OK ==
+ TALER_amount_add (&total_bad_amount_out_plus,
+ &total_bad_amount_out_plus,
+ &roi->details.amount));
+ report (report_wire_out_inconsistencies,
+ json_pack ("{s:I, s:o, s:o, s:o, s:s, s:s}",
+ "row", (json_int_t) rowid,
+ "amount_wired", TALER_JSON_from_amount (&zero),
+ "amount_justified", TALER_JSON_from_amount (amount),
+ "wtid", GNUNET_JSON_from_data_auto (wtid),
+ "timestamp", GNUNET_STRINGS_absolute_time_to_string (date),
+ "diagnostic", "receiver account missmatch"));
+ GNUNET_break (GNUNET_OK ==
+ TALER_amount_add (&total_bad_amount_out_minus,
+ &total_bad_amount_out_minus,
+ amount));
+ goto cleanup;
+ }
if (0 != TALER_amount_cmp (&roi->details.amount,
amount))
{
- report_row_inconsistency ("reserves_out",
- rowid,
- "wire amount missmatch");
- return GNUNET_OK;
+ report (report_wire_out_inconsistencies,
+ json_pack ("{s:I, s:o, s:o, s:o, s:s, s:s}",
+ "row", (json_int_t) rowid,
+ "amount_justified", TALER_JSON_from_amount (amount),
+ "amount_wired", TALER_JSON_from_amount (&roi->details.amount),
+ "wtid", GNUNET_JSON_from_data_auto (wtid),
+ "timestamp", GNUNET_STRINGS_absolute_time_to_string (date),
+ "diagnostic", "wire amount does not match"));
+ if (0 < TALER_amount_cmp (amount,
+ &roi->details.amount))
+ {
+ /* amount > roi->details.amount: wire transfer was smaller than it should have been */
+ struct TALER_Amount delta;
+
+ GNUNET_break (GNUNET_OK ==
+ TALER_amount_subtract (&delta,
+ amount,
+ &roi->details.amount));
+ GNUNET_break (GNUNET_OK ==
+ TALER_amount_add (&total_bad_amount_out_minus,
+ &total_bad_amount_out_minus,
+ &delta));
+ }
+ else
+ {
+ /* roi->details.amount < amount: wire transfer was larger than it should have been */
+ struct TALER_Amount delta;
+
+ GNUNET_break (GNUNET_OK ==
+ TALER_amount_subtract (&delta,
+ &roi->details.amount,
+ amount));
+ GNUNET_break (GNUNET_OK ==
+ TALER_amount_add (&total_bad_amount_out_plus,
+ &total_bad_amount_out_plus,
+ &delta));
+ }
+ goto cleanup;
}
if (roi->details.execution_date.abs_value_us !=
date.abs_value_us)
{
- report_row_minor_inconsistency ("reserves_out",
- rowid,
- "execution date missmatch");
- }
- if (! json_equal ((json_t *) wire,
- roi->details.account_details))
- {
- report_row_inconsistency ("reserves_out",
- rowid,
- "receiver account missmatch");
- return GNUNET_OK;
+ report (report_row_minor_inconsistencies,
+ json_pack ("{s:s, s:I, s:s}",
+ "table", "wire_out",
+ "row", (json_int_t) rowid,
+ "diagnostic", "execution date missmatch"));
}
+cleanup:
GNUNET_assert (GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_remove (out_map,
&key,
@@ -535,11 +634,12 @@ wire_out_cb (void *cls,
/**
- * Complain that we failed to match an entry from #out_map.
+ * Complain that we failed to match an entry from #out_map. This
+ * means a wire transfer was made without proper justification.
*
* @param cls NULL
* @param key unused key
- * @param value the `struct ReserveOutInfo` to free
+ * @param value the `struct ReserveOutInfo` to report
* @return #GNUNET_OK
*/
static int
@@ -549,12 +649,18 @@ complain_out_not_found (void *cls,
{
struct ReserveOutInfo *roi = value;
- (void) roi;
- /* FIXME (#4963): log more precisely which wire transfer (and amount)
- is bogus. */
- report_row_inconsistency ("reserves_out",
- UINT64_MAX,
- "matching wire transfer not found");
+ report (report_wire_out_inconsistencies,
+ json_pack ("{s:I, s:o, s:o, s:o, s:s, s:s}",
+ "row", (json_int_t) 0,
+ "amount_wired", TALER_JSON_from_amount (&roi->details.amount),
+ "amount_justified", TALER_JSON_from_amount (&zero),
+ "wtid", GNUNET_JSON_from_data_auto (&roi->details.reserve_pub), /* #5077 missnomer */
+ "timestamp", GNUNET_STRINGS_absolute_time_to_string (roi->details.execution_date),
+ "diagnostic", "justification for wire transfer not found"));
+ GNUNET_break (GNUNET_OK ==
+ TALER_amount_add (&total_bad_amount_out_plus,
+ &total_bad_amount_out_plus,
+ &roi->details.amount));
return GNUNET_OK;
}
@@ -567,7 +673,7 @@ static void
check_exchange_wire_out ()
{
enum GNUNET_DB_QueryStatus qs;
-
+
qs = edb->select_wire_out_above_serial_id (edb->cls,
esession,
pp.last_wire_out_serial_id,
@@ -582,14 +688,14 @@ check_exchange_wire_out ()
}
GNUNET_CONTAINER_multihashmap_iterate (out_map,
&complain_out_not_found,
- NULL);
- /* clean up (technically redundant, but nicer) */
+ NULL);
+ /* clean up */
GNUNET_CONTAINER_multihashmap_iterate (out_map,
&free_roi,
NULL);
GNUNET_CONTAINER_multihashmap_destroy (out_map);
out_map = NULL;
-
+
/* conclude with: */
commit (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
GNUNET_SCHEDULER_shutdown ();
@@ -616,7 +722,7 @@ history_debit_cb (void *cls,
const struct TALER_WIRE_TransferDetails *details)
{
struct ReserveOutInfo *roi;
-
+
if (TALER_BANK_DIRECTION_NONE == dir)
{
/* end of iteration, now check wire_out to see
@@ -640,9 +746,12 @@ history_debit_cb (void *cls,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
{
GNUNET_break_op (0); /* duplicate wire offset is not allowed! */
- report_row_inconsistency ("bank wire log",
- UINT64_MAX,
- "duplicate wire offset");
+ report (report_row_inconsistencies,
+ json_pack ("{s:s, s:I, s:o, s:s}",
+ "table", "bank wire log",
+ "row", (json_int_t) 0,
+ "wire_offset_hash", GNUNET_JSON_from_data_auto (&roi->subject_hash),
+ "diagnostic", "duplicate wire offset"));
return GNUNET_SYSERR;
}
return GNUNET_OK;
@@ -727,9 +836,12 @@ reserve_in_cb (void *cls,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
{
GNUNET_break_op (0); /* duplicate wire offset is not allowed! */
- report_row_inconsistency ("reserves_in",
- rowid,
- "duplicate wire offset");
+ report (report_row_inconsistencies,
+ json_pack ("{s:s, s:I, s:o, s:s}",
+ "table", "reserves_in",
+ "row", (json_int_t) rowid,
+ "wire_offset_hash", GNUNET_JSON_from_data_auto (&rii->row_off_hash),
+ "diagnostic", "duplicate wire offset"));
return GNUNET_SYSERR;
}
pp.last_reserve_in_serial_id = rowid + 1;
@@ -752,9 +864,18 @@ complain_in_not_found (void *cls,
{
struct ReserveInInfo *rii = value;
- report_row_inconsistency ("reserves_in",
- rii->rowid,
- "matching wire transfer not found");
+ report (report_reserve_in_inconsistencies,
+ json_pack ("{s:I, s:o, s:o, s:o, s:s, s:s}",
+ "row", (json_int_t) rii->rowid,
+ "amount_expected", TALER_JSON_from_amount (&rii->details.amount),
+ "amount_wired", TALER_JSON_from_amount (&zero),
+ "wtid", GNUNET_JSON_from_data_auto (&rii->details.reserve_pub), /* also reserve_pub, but see #5077 missnomer */
+ "timestamp", GNUNET_STRINGS_absolute_time_to_string (rii->details.execution_date),
+ "diagnostic", "incoming wire transfer claimed by exchange not found"));
+ GNUNET_break (GNUNET_OK ==
+ TALER_amount_add (&total_bad_amount_in_minus,
+ &total_bad_amount_in_minus,
+ &rii->details.amount));
return GNUNET_OK;
}
@@ -780,7 +901,7 @@ history_credit_cb (void *cls,
{
struct ReserveInInfo *rii;
struct GNUNET_HashCode key;
-
+
if (TALER_BANK_DIRECTION_NONE == dir)
{
/* end of operation */
@@ -831,42 +952,110 @@ history_credit_cb (void *cls,
if (row_off_size != rii->row_off_size)
{
GNUNET_break (0);
- report_row_inconsistency ("reserves_in",
- rii->rowid,
- "wire reference size missmatch");
- return GNUNET_OK;
- }
- if (0 != TALER_amount_cmp (&rii->details.amount,
- &details->amount))
- {
- report_row_inconsistency ("reserves_in",
- rii->rowid,
- "wire amount missmatch");
+ report (report_row_inconsistencies,
+ json_pack ("{s:s, s:o, s:o, s:s}",
+ "table", "reserves_in",
+ "row", GNUNET_JSON_from_data (row_off, row_off_size),
+ "wire_offset_hash", GNUNET_JSON_from_data_auto (&key),
+ "diagnostic", "wire reference size missmatch"));
return GNUNET_OK;
}
- if (details->execution_date.abs_value_us !=
- rii->details.execution_date.abs_value_us)
- {
- report_row_minor_inconsistency ("reserves_in",
- rii->rowid,
- "execution date missmatch");
- }
if (0 != memcmp (&details->reserve_pub,
&rii->details.reserve_pub,
sizeof (struct TALER_ReservePublicKeyP)))
{
- report_row_inconsistency ("reserves_in",
- rii->rowid,
- "reserve public key / wire subject missmatch");
- return GNUNET_OK;
+ report (report_reserve_in_inconsistencies,
+ json_pack ("{s:I, s:o, s:o, s:o, s:s, s:s}",
+ "row", GNUNET_JSON_from_data (row_off, row_off_size),
+ "amount_exchange_expected", TALER_JSON_from_amount (&rii->details.amount),
+ "amount_wired", TALER_JSON_from_amount (&zero),
+ "wtid", GNUNET_JSON_from_data_auto (&rii->details.reserve_pub), /* #5077 missnomer */
+ "timestamp", GNUNET_STRINGS_absolute_time_to_string (rii->details.execution_date),
+ "diagnostic", "wire subject does not match"));
+ GNUNET_break (GNUNET_OK ==
+ TALER_amount_add (&total_bad_amount_in_minus,
+ &total_bad_amount_in_minus,
+ &rii->details.amount));
+ report (report_reserve_in_inconsistencies,
+ json_pack ("{s:I, s:o, s:o, s:o, s:s, s:s}",
+ "row", GNUNET_JSON_from_data (row_off, row_off_size),
+ "amount_exchange_expected", TALER_JSON_from_amount (&zero),
+ "amount_wired", TALER_JSON_from_amount (&details->amount),
+ "wtid", GNUNET_JSON_from_data_auto (&details->reserve_pub), /* #5077 missnomer */
+ "timestamp", GNUNET_STRINGS_absolute_time_to_string (details->execution_date),
+ "diagnostic", "wire subject does not match"));
+
+ GNUNET_break (GNUNET_OK ==
+ TALER_amount_add (&total_bad_amount_in_plus,
+ &total_bad_amount_in_plus,
+ &details->amount));
+ goto cleanup;
+ }
+ if (0 != TALER_amount_cmp (&rii->details.amount,
+ &details->amount))
+ {
+ report (report_reserve_in_inconsistencies,
+ json_pack ("{s:I, s:o, s:o, s:o, s:s, s:s}",
+ "row", GNUNET_JSON_from_data (row_off, row_off_size),
+ "amount_exchange_expected", TALER_JSON_from_amount (&rii->details.amount),
+ "amount_wired", TALER_JSON_from_amount (&details->amount),
+ "wtid", GNUNET_JSON_from_data_auto (&details->reserve_pub), /* #5077 missnomer */
+ "timestamp", GNUNET_STRINGS_absolute_time_to_string (details->execution_date),
+ "diagnostic", "wire amount does not match"));
+ if (0 < TALER_amount_cmp (&details->amount,
+ &rii->details.amount))
+ {
+ /* details->amount > rii->details.amount: wire transfer was larger than it should have been */
+ struct TALER_Amount delta;
+
+ GNUNET_break (GNUNET_OK ==
+ TALER_amount_subtract (&delta,
+ &details->amount,
+ &rii->details.amount));
+ GNUNET_break (GNUNET_OK ==
+ TALER_amount_add (&total_bad_amount_in_plus,
+ &total_bad_amount_in_plus,
+ &delta));
+ }
+ else
+ {
+ /* rii->details.amount < details->amount: wire transfer was smaller than it should have been */
+ struct TALER_Amount delta;
+
+ GNUNET_break (GNUNET_OK ==
+ TALER_amount_subtract (&delta,
+ &rii->details.amount,
+ &details->amount));
+ GNUNET_break (GNUNET_OK ==
+ TALER_amount_add (&total_bad_amount_in_minus,
+ &total_bad_amount_in_minus,
+ &delta));
+ }
+ goto cleanup;
}
if (! json_equal (details->account_details,
rii->details.account_details))
{
- report_row_minor_inconsistency ("reserves_in",
- rii->rowid,
- "sender account missmatch");
+ report (report_missattribution_in_inconsistencies,
+ json_pack ("{s:s, s:o, s:o}",
+ "amount", TALER_JSON_from_amount (&rii->details.amount),
+ "row", GNUNET_JSON_from_data (row_off, row_off_size),
+ "wtid", GNUNET_JSON_from_data_auto (&rii->details.reserve_pub))); /* FIXME #5077 missnomer */
+ GNUNET_break (GNUNET_OK ==
+ TALER_amount_add (&total_missattribution_in,
+ &total_missattribution_in,
+ &rii->details.amount));
+ }
+ if (details->execution_date.abs_value_us !=
+ rii->details.execution_date.abs_value_us)
+ {
+ report (report_row_minor_inconsistencies,
+ json_pack ("{s:s, s:o, s:s}",
+ "table", "reserves_in",
+ "row", GNUNET_JSON_from_data (row_off, row_off_size),
+ "diagnostic", "execution date missmatch"));
}
+ cleanup:
GNUNET_assert (GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_remove (in_map,
&key,
@@ -1002,9 +1191,32 @@ run (void *cls,
return;
}
GNUNET_assert (NULL !=
- (report_row_inconsistencies = json_array ()));
+ (report_wire_out_inconsistencies = json_array ()));
+ GNUNET_assert (NULL !=
+ (report_reserve_in_inconsistencies = json_array ()));
GNUNET_assert (NULL !=
(report_row_minor_inconsistencies = json_array ()));
+ GNUNET_assert (NULL !=
+ (report_row_inconsistencies = json_array ()));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_get_zero (currency,
+ &total_bad_amount_out_plus));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_get_zero (currency,
+ &total_bad_amount_out_minus));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_get_zero (currency,
+ &total_bad_amount_in_plus));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_get_zero (currency,
+ &total_bad_amount_in_minus));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_get_zero (currency,
+ &total_missattribution_in));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_get_zero (currency,
+ &zero));
+
qsx = adb->get_wire_auditor_progress (adb->cls,
asession,
&master_pub,