update auditor report format to capture nicely all diagnostics that may currently be generated by the auditor (closes #4962)

This commit is contained in:
Christian Grothoff 2017-11-06 19:11:43 +01:00
parent cc09fbbb29
commit 45c443f348
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
2 changed files with 458 additions and 114 deletions

View File

@ -108,31 +108,39 @@ compromise.
{% endif %} {% endif %}
\subsection{Arithmetic problems}
This section lists cases where the arithmetic of the exchange
involving amounts disagrees with the arithmetic of the auditor.
Disagreements imply that either the exchange made a loss (sending out
too much money), or screwed a customer (and thus at least needs to fix
the financial damage done to the customer).
\begin{longtable}{p{1.5cm}|rl|rl|p{4cm}} \begin{longtable}{p{5.5cm}|l|rl|rl}
{\bf Reserve} & \multicolumn{2}{|c|}{ {\bf Expected}} & \multicolumn{2}{|c|}{ {\bf Observed}} & {\bf Diagnostic} \\ \hline \hline {\bf Operation} & {\bf Table row} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c|}{ {\bf Auditor}} \\
\hline \hline
\endfirsthead \endfirsthead
{\bf Reserve} & \multicolumn{2}{|c|}{ {\bf Expected}} & \multicolumn{2}{|c|}{ {\bf Observed}} & {\bf Diagnostic} \\ \hline \hline {\bf Operation} & {\bf Table row} & \ \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c|}{ {\bf Auditor}} \\ \hline \hline
\endhead \endhead
\hline \hline \hline \hline
{\bf Reserve} & \multicolumn{2}{|c|}{ {\bf Expected}} & \multicolumn{2}{|c|}{ {\bf Observed}} & {\bf Diagnostic} \\ {\bf Operation} & {\bf Table row} & \ \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c|}{ {\bf Auditor}} \\
\endfoot \endfoot
\hline \hline
% FIXME: replace these with the summary column adding up the amounts! {\bf Total} & &
{\bf Reserve} & \multicolumn{2}{|c|}{ {\bf Expected}} & \multicolumn{2}{|c|}{ {\bf Observed}} & {\bf Diagnostic} \\ {{ data.total_arithmetic_delta_plus.value }}.{{ data.total_arithmetic_delta_plus.fraction }} &
\caption{Reserve inconsistencies.} {{ data.total_arithmetic_delta_plus.currency }} &
\label{table:reserve:inconsistencies} {{ data.total_arithmetic_delta_minus.value }}.{{ data.total_arithmetic_delta_minus.fraction }} &
{{ data.total_arithmetic_delta_minus.currency }} &
\caption{Arithmetic inconsistencies.}
\label{table:amount:arithmetic:inconsistencies}
\endlastfoot \endlastfoot
{% for item in data.reserve_inconsistencies %} {% for item in data.amount_arithmetic_inconsistencies %}
\multicolumn{6}{l}{ {\tt {{ item.reserve_pub }} } } \\ {{ item.operation }} &
\nopagebreak {{ item.rowid }} &
& {{ item.exchange.value }}.{{ item.exchange.fraction }} &
{{ item.expected.value }}.{{ item.expected.fraction }} & {{ item.exchange.currency }} &
{{ item.expected.currency }} & {{ item.auditor.value }}.{{ item.auditor.fraction }} &
{{ item.observed.value }}.{{ item.observed.fraction }} & {{ item.auditor.currency }} \\ \hline
{{ item.observed.currency }} &
{{ item.diagnostic }} \\ \hline
{% endfor %} {% endfor %}
\end{longtable} \end{longtable}
@ -172,7 +180,7 @@ compromise resulting in proportional financial losses to the exchange.
{% endif %} {% endif %}
\subsection{Claimed outgoing wire transfers} \subsection{Claimed outgoing wire transfer inconsistencies}
This section is about the exchange's database containing a This section is about the exchange's database containing a
justification to make an outgoing wire transfer for an aggregated justification to make an outgoing wire transfer for an aggregated
@ -217,7 +225,88 @@ would be reported separately in Section~\ref{sec:wire_check_out}.
\subsection{Coin history inconsistencies} \subsection{Coin history inconsistencies}
TODO. This section lists cases where the exchange made arithmetic errors found when
looking at the transaction history of a coin. The totals sum up the differences
in amounts that matter for profit/loss calculations of the exchange. When an
exchange merely shifted money from customers to merchants (or vice versa) without
any effects on its own balance, those entries are excluded from the total.
{% if data.coin_inconsistencies|length() == 0 %}
{\bf All coin histories were unproblematic.}
{% else %}
\begin{longtable}{l|p{5.5cm}|rl|rl}
{\bf Operation} & {\bf Coin public key} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c|}{ {\bf Auditor}} \\
\hline \hline
\endfirsthead
{\bf Operation} & {\bf Coin public key} & \ \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c|}{ {\bf Auditor}} \\ \hline \hline
\endhead
\hline \hline
{\bf Operation} & {\bf Coin public key} & \ \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c|}{ {\bf Auditor}} \\
\endfoot
\hline
{\bf Total} & &
{{ data.total_coin_delta_plus.value }}.{{ data.total_coin_delta_plus.fraction }} &
{{ data.total_coin_delta_plus.currency }} &
- {{ data.total_coin_delta_minus.value }}.{{ data.total_coin_delta_minus.fraction }} &
{{ data.total_coin_delta_minus.currency }} &
\caption{Arithmetic inconsistencies of amount calculations involving a coin.}
\label{table:amount:arithmetic:coin:inconsistencies}
\endlastfoot
{% for item in data.coin_inconsistencies %}
{{ item.operation }} &
\multicolumn{5}{l}{ {\tt {{ item.coin_pub }} } } \\
\nopagebreak & &
{{ item.exchange.value }}.{{ item.exchange.fraction }} &
{{ item.exchange.currency }} &
{{ item.auditor.value }}.{{ item.auditor.fraction }} &
{{ item.auditor.currency }} \\ \hline
{% endfor %}
\end{longtable}
{% endif %}
\subsection{Operations with bad signatures}
This section lists operations that the exchange performed, but for
which the signatures provided are invalid. Hence the operations were
invalid and the amount involved should be considered lost.
The key given is always the key for which the signature verification
step failed. This is the reserve public key for ``withdraw''
operations, the coin public key for ``deposit'' and ``melt''
operations, the merchant's public key for ``melt'' operations,
the (hash of the) denomination public key for
``payback-verify'' and ``deposit-verify'' operations, and the master
public key for ``payback-master'' operations.
{% if data.reserve_wire_out_inconsistencies|length() == 0 %}
{\bf All signatures were valid.}
{% else %}
\begin{longtable}{p{1.5cm}|c|l|rl}
{\bf Public key} & {\bf Operation type} & Database row & \multicolumn{2}{|c|}{ {\bf Loss amount}} \\
\hline \hline
\endfirsthead
{\bf Public key} & {\bf Operation type} & Database row & \multicolumn{2}{|c|}{ {\bf Loss amount}} \\ \hline \hline
\endhead
\hline \hline
{\bf Public key} & {\bf Operation type} & Database row & \multicolumn{2}{|c|}{ {\bf Loss amount}} \\
\endfoot
\hline
{\bf Total losses} & & &
{{ data.total_bad_sig_loss.value}}.{{ data.total_bad_sig_loss.fraction}} & {{ data.total_bad_sig_loss.currency}} \\
\caption{Losses from operations performed on coins without proper signatures.}
\label{table:bad_signature_losses}
\endlastfoot
{% for item in data.bad_sig_losses %}
\multicolumn{5}{l}{ {\tt {{ item.key_pub }} } } \\
\nopagebreak
& {{ item.operation }} & {{ item.rowid }} &
{{ item.loss.value }}.{{ item.loss.fraction }} &
{{ item.loss.currency }} \\ \hline
{% endfor %}
\end{longtable}
{% endif %}
\subsection{Actual incoming wire transfers} \subsection{Actual incoming wire transfers}
@ -269,6 +358,38 @@ translate into a financial loss (yet).
{% endif %} {% endif %}
\subsection{Other issues}
This section describes issues found that do not have a clear financial
impact.
{% if data.row_inconsistencies|length() == 0 %}
{\bf No 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 issues found (by table and row).}
\label{table:misc}
\endlastfoot
{% for item in data.row_inconsistencies %}
{{ item.table }} &
{{ item.row }} &
{{ item.diagnostic }} \\ \hline
{% endfor %}
\end{longtable}
{% endif %}
\section{Delays and timing} \section{Delays and timing}
This section describes issues that are likely caused simply by This section describes issues that are likely caused simply by

View File

@ -189,11 +189,37 @@ static struct TALER_Amount total_wire_out_delta_minus;
*/ */
static json_t *report_coin_inconsistencies; static json_t *report_coin_inconsistencies;
/**
* Profits the exchange made by bad amount calculations on coins.
*/
static struct TALER_Amount total_coin_delta_plus;
/**
* Losses the exchange made by bad amount calculations on coins.
*/
static struct TALER_Amount total_coin_delta_minus;
/** /**
* Report about aggregate wire transfer fee profits. * Report about aggregate wire transfer fee profits.
*/ */
static json_t *report_aggregation_fee_balances; static json_t *report_aggregation_fee_balances;
/**
* Report about amount calculation differences (causing profit
* or loss at the exchange).
*/
static json_t *report_amount_arithmetic_inconsistencies;
/**
* Profits the exchange made by bad amount calculations.
*/
static struct TALER_Amount total_arithmetic_delta_plus;
/**
* Losses the exchange made by bad amount calculations.
*/
static struct TALER_Amount total_arithmetic_delta_minus;
/** /**
* Total amount reported in all calls to #report_emergency(). * Total amount reported in all calls to #report_emergency().
*/ */
@ -234,6 +260,15 @@ static struct TALER_Amount total_refund_fee_income;
*/ */
static struct TALER_Amount total_aggregation_fee_income; static struct TALER_Amount total_aggregation_fee_income;
/**
* Array of reports about coin operations with bad signatures.
*/
static json_t *report_bad_sig_losses;
/**
* Total amount lost by operations for which signatures were invalid.
*/
static struct TALER_Amount total_bad_sig_loss;
/* ***************************** Report logic **************************** */ /* ***************************** Report logic **************************** */
@ -290,6 +325,128 @@ report_emergency (const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki
} }
/**
* Report a (serious) inconsistency in the exchange's database with
* respect to calculations involving amounts.
*
* @param operation what operation had the inconsistency
* @param rowid affected row, UINT64_MAX if row is missing
* @param exchange amount calculated by exchange
* @param auditor amount calculated by auditor
* @param proftable 1 if @a exchange being larger than @a auditor is
* profitable for the exchange for this operation,
* -1 if @a exchange being smaller than @a auditor is
* profitable for the exchange, and 0 if it is unclear
*/
static void
report_amount_arithmetic_inconsistency (const char *operation,
uint64_t rowid,
const struct TALER_Amount *exchange,
const struct TALER_Amount *auditor,
int profitable)
{
struct TALER_Amount delta;
struct TALER_Amount *target;
if (0 < TALER_amount_cmp (exchange,
auditor))
{
/* exchange > auditor */
GNUNET_break (GNUNET_OK ==
TALER_amount_subtract (&delta,
exchange,
auditor));
}
else
{
/* auditor < exchange */
profitable = - profitable;
GNUNET_break (GNUNET_OK ==
TALER_amount_subtract (&delta,
auditor,
exchange));
}
report (report_amount_arithmetic_inconsistencies,
json_pack ("{s:s, s:I, s:o, s:o, s:I}",
"operation", operation,
"rowid", (json_int_t) rowid,
"exchange", TALER_JSON_from_amount (exchange),
"auditor", TALER_JSON_from_amount (auditor),
"profitable", (json_int_t) profitable));
if (0 != profitable)
{
target = profitable
? &total_arithmetic_delta_plus
: &total_arithmetic_delta_minus;
GNUNET_break (GNUNET_OK ==
TALER_amount_add (target,
target,
&delta));
}
}
/**
* Report a (serious) inconsistency in the exchange's database with
* respect to calculations involving amounts of a coin.
*
* @param operation what operation had the inconsistency
* @param coin_pub affected coin
* @param exchange amount calculated by exchange
* @param auditor amount calculated by auditor
* @param proftable 1 if @a exchange being larger than @a auditor is
* profitable for the exchange for this operation,
* -1 if @a exchange being smaller than @a auditor is
* profitable for the exchange, and 0 if it is unclear
*/
static void
report_coin_arithmetic_inconsistency (const char *operation,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *exchange,
const struct TALER_Amount *auditor,
int profitable)
{
struct TALER_Amount delta;
struct TALER_Amount *target;
if (0 < TALER_amount_cmp (exchange,
auditor))
{
/* exchange > auditor */
GNUNET_break (GNUNET_OK ==
TALER_amount_subtract (&delta,
exchange,
auditor));
}
else
{
/* auditor < exchange */
profitable = - profitable;
GNUNET_break (GNUNET_OK ==
TALER_amount_subtract (&delta,
auditor,
exchange));
}
report (report_coin_inconsistencies,
json_pack ("{s:s, s:o, s:o, s:o, s:I}",
"operation", operation,
"coin_pub", GNUNET_JSON_from_data_auto (coin_pub),
"exchange", TALER_JSON_from_amount (exchange),
"auditor", TALER_JSON_from_amount (auditor),
"profitable", (json_int_t) profitable));
if (0 != profitable)
{
target = profitable
? &total_coin_delta_plus
: &total_coin_delta_minus;
GNUNET_break (GNUNET_OK ==
TALER_amount_add (target,
target,
&delta));
}
}
/** /**
* Report a (serious) inconsistency in the exchange's database. * Report a (serious) inconsistency in the exchange's database.
* *
@ -310,33 +467,6 @@ report_row_inconsistency (const char *table,
} }
/**
* Report a global inconsistency with respect to a coin's history.
*
* @param coin_pub the affected coin
* @param expected expected amount
* @param observed observed amount
* @param diagnostic message explaining what @a expected and @a observed refer to
*/
static void
report_coin_inconsistency (const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *expected,
const struct TALER_Amount *observed,
const char *diagnostic)
{
report (report_coin_inconsistencies,
json_pack ("{s:o, s:o, s:o, s:s}",
"coin_pub",
GNUNET_JSON_from_data_auto (coin_pub),
"expected",
TALER_JSON_from_amount (expected),
"observed",
TALER_JSON_from_amount (observed),
"diagnostic",
diagnostic));
}
/* ************************* Transaction-global state ************************ */ /* ************************* Transaction-global state ************************ */
/** /**
@ -765,9 +895,16 @@ handle_reserve_out (void *cls,
&reserve_sig->eddsa_signature, &reserve_sig->eddsa_signature,
&reserve_pub->eddsa_pub)) &reserve_pub->eddsa_pub))
{ {
report_row_inconsistency ("withdraw", report (report_bad_sig_losses,
rowid, json_pack ("{s:s, s:I, s:o, s:o}",
"invalid signature for withdrawal"); "operation", "withdraw",
"row", (json_int_t) rowid,
"loss", TALER_JSON_from_amount (amount_with_fee),
"key_pub", GNUNET_JSON_from_data_auto (reserve_pub)));
GNUNET_break (GNUNET_OK ==
TALER_amount_add (&total_bad_sig_loss,
&total_bad_sig_loss,
amount_with_fee));
return GNUNET_OK; return GNUNET_OK;
} }
@ -860,19 +997,26 @@ handle_payback_by_reserve (void *cls,
/* should be monotonically increasing */ /* should be monotonically increasing */
GNUNET_assert (rowid >= pp.last_reserve_payback_serial_id); GNUNET_assert (rowid >= pp.last_reserve_payback_serial_id);
pp.last_reserve_payback_serial_id = rowid + 1; pp.last_reserve_payback_serial_id = rowid + 1;
GNUNET_CRYPTO_rsa_public_key_hash (coin->denom_pub.rsa_public_key,
&pr.h_denom_pub);
if (GNUNET_OK != if (GNUNET_OK !=
TALER_test_coin_valid (coin)) TALER_test_coin_valid (coin))
{ {
report_row_inconsistency ("payback", report (report_bad_sig_losses,
rowid, json_pack ("{s:s, s:I, s:o, s:o}",
"coin denomination signature invalid"); "operation", "payback-verify",
"row", (json_int_t) rowid,
"loss", TALER_JSON_from_amount (amount),
"key_pub", GNUNET_JSON_from_data_auto (&pr.h_denom_pub)));
GNUNET_break (GNUNET_OK ==
TALER_amount_add (&total_bad_sig_loss,
&total_bad_sig_loss,
amount));
} }
pr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_PAYBACK); pr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_PAYBACK);
pr.purpose.size = htonl (sizeof (pr)); pr.purpose.size = htonl (sizeof (pr));
pr.coin_pub = coin->coin_pub; pr.coin_pub = coin->coin_pub;
GNUNET_CRYPTO_rsa_public_key_hash (coin->denom_pub.rsa_public_key,
&pr.h_denom_pub);
pr.coin_blind = *coin_blind; pr.coin_blind = *coin_blind;
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_PAYBACK, GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_PAYBACK,
@ -880,9 +1024,16 @@ handle_payback_by_reserve (void *cls,
&coin_sig->eddsa_signature, &coin_sig->eddsa_signature,
&coin->coin_pub.eddsa_pub)) &coin->coin_pub.eddsa_pub))
{ {
report_row_inconsistency ("payback", report (report_bad_sig_losses,
rowid, json_pack ("{s:s, s:I, s:o, s:o}",
"coin payback signature invalid"); "operation", "payback",
"row", (json_int_t) rowid,
"loss", TALER_JSON_from_amount (amount),
"key_pub", GNUNET_JSON_from_data_auto (&coin->coin_pub)));
GNUNET_break (GNUNET_OK ==
TALER_amount_add (&total_bad_sig_loss,
&total_bad_sig_loss,
amount));
} }
/* check that the coin was eligible for payback!*/ /* check that the coin was eligible for payback!*/
@ -921,9 +1072,6 @@ handle_payback_by_reserve (void *cls,
&msig.eddsa_signature, &msig.eddsa_signature,
&master_pub.eddsa_pub)) &master_pub.eddsa_pub))
{ {
report_row_inconsistency ("denomination_revocations",
rev_rowid,
"master signature invalid");
rev = "master signature invalid"; rev = "master signature invalid";
} }
else else
@ -937,6 +1085,19 @@ handle_payback_by_reserve (void *cls,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
} }
} }
if (0 == strcmp (rev, "master signature invalid"))
{
report (report_bad_sig_losses,
json_pack ("{s:s, s:I, s:o, s:o}",
"operation", "payback-master",
"row", (json_int_t) rev_rowid,
"loss", TALER_JSON_from_amount (amount),
"key_pub", GNUNET_JSON_from_data_auto (&master_pub)));
GNUNET_break (GNUNET_OK ==
TALER_amount_add (&total_bad_sig_loss,
&total_bad_sig_loss,
amount));
}
GNUNET_CRYPTO_hash (reserve_pub, GNUNET_CRYPTO_hash (reserve_pub,
sizeof (*reserve_pub), sizeof (*reserve_pub),
@ -1833,10 +1994,11 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
&refunds)) &refunds))
{ {
/* refunds above expenditures? Bad! */ /* refunds above expenditures? Bad! */
report_coin_inconsistency (coin_pub, report_coin_arithmetic_inconsistency ("refund (balance)",
coin_pub,
&expenditures, &expenditures,
&refunds, &refunds,
"could not subtract refunded amount from expenditures"); 0);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
@ -1847,10 +2009,11 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
&value)) &value))
{ {
/* spent > value */ /* spent > value */
report_coin_inconsistency (coin_pub, report_coin_arithmetic_inconsistency ("spend",
coin_pub,
&spent, &spent,
&value, &value,
"accepted deposits (minus refunds) exceeds denomination value"); -1);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
@ -1861,10 +2024,11 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
&merchant_loss)) &merchant_loss))
{ {
/* refunds above deposits? Bad! */ /* refunds above deposits? Bad! */
report_coin_inconsistency (coin_pub, report_coin_arithmetic_inconsistency ("refund (merchant)",
coin_pub,
merchant_gain, merchant_gain,
&merchant_loss, &merchant_loss,
"merchant was granted more refunds than he deposited"); 0);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@ -1905,7 +2069,6 @@ wire_transfer_information_cb (void *cls,
{ {
struct WireCheckContext *wcc = cls; struct WireCheckContext *wcc = cls;
const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki; const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
struct TALER_Amount contribution;
struct TALER_Amount computed_value; struct TALER_Amount computed_value;
struct TALER_Amount computed_fees; struct TALER_Amount computed_fees;
struct TALER_Amount coin_value_without_fee; struct TALER_Amount coin_value_without_fee;
@ -1977,9 +2140,11 @@ wire_transfer_information_cb (void *cls,
coin_fee)) coin_fee))
{ {
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR; wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
report_row_inconsistency ("aggregation", report_amount_arithmetic_inconsistency ("aggregation (fee structure)",
rowid, rowid,
"inconsistent coin value and fee claimed in aggregation"); coin_value,
coin_fee,
-1);
return; return;
} }
if (0 != if (0 !=
@ -1987,18 +2152,22 @@ wire_transfer_information_cb (void *cls,
&coin_value_without_fee)) &coin_value_without_fee))
{ {
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR; wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
report_row_inconsistency ("aggregation", report_amount_arithmetic_inconsistency ("aggregation (contribution)",
rowid, rowid,
"coin transaction history and aggregation disagree about coin's contribution"); &coin_value_without_fee,
&computed_value,
-1);
} }
if (0 != if (0 !=
TALER_amount_cmp (&computed_fees, TALER_amount_cmp (&computed_fees,
coin_fee)) coin_fee))
{ {
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR; wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
report_row_inconsistency ("aggregation", report_amount_arithmetic_inconsistency ("aggregation (fee)",
rowid, rowid,
"coin transaction history and aggregation disagree about applicable fees"); coin_fee,
&computed_fees,
1);
} }
edb->free_coin_transaction_list (edb->cls, edb->free_coin_transaction_list (edb->cls,
tl); tl);
@ -2032,23 +2201,12 @@ wire_transfer_information_cb (void *cls,
"date given in aggregate does not match wire transfer date"); "date given in aggregate does not match wire transfer date");
return; return;
} }
if (GNUNET_SYSERR ==
TALER_amount_subtract (&contribution,
coin_value,
coin_fee))
{
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
report_row_inconsistency ("aggregation",
rowid,
"could not calculate contribution of coin");
return;
}
/* Add coin's contribution to total aggregate value */ /* Add coin's contribution to total aggregate value */
if (GNUNET_OK != if (GNUNET_OK !=
TALER_amount_add (&wcc->total_deposits, TALER_amount_add (&wcc->total_deposits,
&wcc->total_deposits, &wcc->total_deposits,
&contribution)) &coin_value_without_fee))
{ {
GNUNET_break (0); GNUNET_break (0);
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR; wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
@ -2229,6 +2387,7 @@ check_wire_out_cb (void *cls,
} }
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != wcc.qs) if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != wcc.qs)
{ {
/* FIXME: can we provide a more detailed error report? */
report_row_inconsistency ("wire_out", report_row_inconsistency ("wire_out",
rowid, rowid,
"audit of associated transactions failed"); "audit of associated transactions failed");
@ -2250,9 +2409,11 @@ check_wire_out_cb (void *cls,
&wcc.total_deposits, &wcc.total_deposits,
wire_fee)) wire_fee))
{ {
report_row_inconsistency ("wire_out", report_amount_arithmetic_inconsistency ("wire out (fee structure)",
rowid, rowid,
"could not subtract wire fee from total amount"); &wcc.total_deposits,
wire_fee,
-1);
return GNUNET_OK; return GNUNET_OK;
} }
@ -2852,9 +3013,16 @@ refresh_session_cb (void *cls,
&coin_sig->eddsa_signature, &coin_sig->eddsa_signature,
&coin_pub->eddsa_pub)) &coin_pub->eddsa_pub))
{ {
report_row_inconsistency ("melt", report (report_bad_sig_losses,
rowid, json_pack ("{s:s, s:I, s:o, s:o}",
"invalid signature for coin melt"); "operation", "melt",
"row", (json_int_t) rowid,
"loss", TALER_JSON_from_amount (amount_with_fee),
"key_pub", GNUNET_JSON_from_data_auto (coin_pub)));
GNUNET_break (GNUNET_OK ==
TALER_amount_add (&total_bad_sig_loss,
&total_bad_sig_loss,
amount_with_fee));
return GNUNET_OK; return GNUNET_OK;
} }
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@ -2952,9 +3120,11 @@ refresh_session_cb (void *cls,
&amount_without_fee)) &amount_without_fee))
{ {
/* refresh_cost > amount_without_fee */ /* refresh_cost > amount_without_fee */
report_row_inconsistency ("melt", report_amount_arithmetic_inconsistency ("melt (fee)",
rowid, rowid,
"refresh costs exceed value of melt"); &amount_without_fee,
&refresh_cost,
-1);
return GNUNET_OK; return GNUNET_OK;
} }
@ -3160,9 +3330,16 @@ deposit_cb (void *cls,
&coin_sig->eddsa_signature, &coin_sig->eddsa_signature,
&coin_pub->eddsa_pub)) &coin_pub->eddsa_pub))
{ {
report_row_inconsistency ("deposit", report (report_bad_sig_losses,
rowid, json_pack ("{s:s, s:I, s:o, s:o}",
"invalid signature for coin deposit"); "operation", "deposit",
"row", (json_int_t) rowid,
"loss", TALER_JSON_from_amount (amount_with_fee),
"key_pub", GNUNET_JSON_from_data_auto (coin_pub)));
GNUNET_break (GNUNET_OK ==
TALER_amount_add (&total_bad_sig_loss,
&total_bad_sig_loss,
amount_with_fee));
return GNUNET_OK; return GNUNET_OK;
} }
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@ -3293,9 +3470,16 @@ refund_cb (void *cls,
&merchant_sig->eddsa_sig, &merchant_sig->eddsa_sig,
&merchant_pub->eddsa_pub)) &merchant_pub->eddsa_pub))
{ {
report_row_inconsistency ("refund", report (report_bad_sig_losses,
rowid, json_pack ("{s:s, s:I, s:o, s:o}",
"invalid signature for refund"); "operation", "refund",
"row", (json_int_t) rowid,
"loss", TALER_JSON_from_amount (amount_with_fee),
"key_pub", GNUNET_JSON_from_data_auto (merchant_pub)));
GNUNET_break (GNUNET_OK ==
TALER_amount_add (&total_bad_sig_loss,
&total_bad_sig_loss,
amount_with_fee));
return GNUNET_OK; return GNUNET_OK;
} }
@ -3306,9 +3490,11 @@ refund_cb (void *cls,
amount_with_fee, amount_with_fee,
&refund_fee)) &refund_fee))
{ {
report_row_inconsistency ("refund", report_amount_arithmetic_inconsistency ("refund (fee)",
rowid, rowid,
"refunded amount smaller than refund fee"); &amount_without_fee,
&refund_fee,
-1);
return GNUNET_OK; return GNUNET_OK;
} }
@ -3823,9 +4009,24 @@ run (void *cls,
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (currency, TALER_amount_get_zero (currency,
&total_wire_out_delta_minus)); &total_wire_out_delta_minus));
GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (currency,
&total_arithmetic_delta_plus));
GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (currency,
&total_arithmetic_delta_minus));
GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (currency,
&total_coin_delta_plus));
GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (currency,
&total_coin_delta_minus));
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (currency, TALER_amount_get_zero (currency,
&total_balance_reserve_not_closed)); &total_balance_reserve_not_closed));
GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (currency,
&total_bad_sig_loss));
GNUNET_assert (NULL != GNUNET_assert (NULL !=
(report_emergencies = json_array ())); (report_emergencies = json_array ()));
GNUNET_assert (NULL != GNUNET_assert (NULL !=
@ -3844,6 +4045,10 @@ run (void *cls,
(report_coin_inconsistencies = json_array ())); (report_coin_inconsistencies = json_array ()));
GNUNET_assert (NULL != GNUNET_assert (NULL !=
(report_aggregation_fee_balances = json_array ())); (report_aggregation_fee_balances = json_array ()));
GNUNET_assert (NULL !=
(report_amount_arithmetic_inconsistencies = json_array ()));
GNUNET_assert (NULL !=
(report_bad_sig_losses = json_array ()));
setup_sessions_and_run (); setup_sessions_and_run ();
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Audit complete\n"); "Audit complete\n");
@ -3866,7 +4071,9 @@ run (void *cls,
" s:o, s:o, s:o, s:o, s:o," " s:o, s:o, s:o, s:o, s:o,"
" s:o, s:o, s:o, s:o, s:o," " s:o, s:o, s:o, s:o, s:o,"
" s:o, s:o, s:o, s:o, s:o," " s:o, s:o, s:o, s:o, s:o,"
" s:o, s:o, s:o }", " s:o, s:o, s:o, s:o, s:o,"
" s:o, s:o, s:o, s:o, s:o,"
" s:o}",
/* blocks of 5 for easier counting/matching to format string */ /* blocks of 5 for easier counting/matching to format string */
/* block */ /* block */
"reserve_balance_insufficient_inconsistencies", "reserve_balance_insufficient_inconsistencies",
@ -3911,12 +4118,28 @@ run (void *cls,
"total_wire_out_delta_minus", "total_wire_out_delta_minus",
TALER_JSON_from_amount (&total_wire_out_delta_minus), TALER_JSON_from_amount (&total_wire_out_delta_minus),
/* block */ /* block */
"bad_sig_losses",
report_bad_sig_losses,
"total_bad_sig_loss",
TALER_JSON_from_amount (&total_bad_sig_loss),
"row_inconsistencies", "row_inconsistencies",
report_row_inconsistencies, report_row_inconsistencies,
"denomination_key_validity_withdraw_inconsistencies", "denomination_key_validity_withdraw_inconsistencies",
denomination_key_validity_withdraw_inconsistencies, denomination_key_validity_withdraw_inconsistencies,
"coin_inconsistencies", "coin_inconsistencies",
report_coin_inconsistencies, report_coin_inconsistencies,
/* block */
"total_coin_delta_plus",
TALER_JSON_from_amount (&total_coin_delta_plus),
"total_coin_delta_minus",
TALER_JSON_from_amount (&total_coin_delta_minus),
"amount_arithmetic_inconsistencies",
report_amount_arithmetic_inconsistencies,
"total_arithmetic_delta_plus",
TALER_JSON_from_amount (&total_arithmetic_delta_plus),
"total_arithmetic_delta_minus",
TALER_JSON_from_amount (&total_arithmetic_delta_minus),
/* block */
"total_aggregation_fee_income", "total_aggregation_fee_income",
TALER_JSON_from_amount (&total_aggregation_fee_income) TALER_JSON_from_amount (&total_aggregation_fee_income)
); );