aboutsummaryrefslogtreecommitdiff
path: root/src/auditor
diff options
context:
space:
mode:
Diffstat (limited to 'src/auditor')
-rw-r--r--src/auditor/taler-auditor.c411
1 files changed, 317 insertions, 94 deletions
diff --git a/src/auditor/taler-auditor.c b/src/auditor/taler-auditor.c
index 1190e5c34..e801ec77b 100644
--- a/src/auditor/taler-auditor.c
+++ b/src/auditor/taler-auditor.c
@@ -190,11 +190,37 @@ static struct TALER_Amount total_wire_out_delta_minus;
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.
*/
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().
*/
static struct TALER_Amount reported_emergency_sum;
@@ -234,6 +260,15 @@ static struct TALER_Amount total_refund_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 **************************** */
@@ -291,6 +326,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.
*
* @param table affected table
@@ -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 ************************ */
/**
@@ -765,9 +895,16 @@ handle_reserve_out (void *cls,
&reserve_sig->eddsa_signature,
&reserve_pub->eddsa_pub))
{
- report_row_inconsistency ("withdraw",
- rowid,
- "invalid signature for withdrawal");
+ report (report_bad_sig_losses,
+ json_pack ("{s:s, s:I, s:o, s:o}",
+ "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;
}
@@ -860,19 +997,26 @@ handle_payback_by_reserve (void *cls,
/* should be monotonically increasing */
GNUNET_assert (rowid >= pp.last_reserve_payback_serial_id);
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 !=
TALER_test_coin_valid (coin))
{
- report_row_inconsistency ("payback",
- rowid,
- "coin denomination signature invalid");
+ report (report_bad_sig_losses,
+ json_pack ("{s:s, s:I, s:o, s:o}",
+ "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.size = htonl (sizeof (pr));
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;
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_PAYBACK,
@@ -880,9 +1024,16 @@ handle_payback_by_reserve (void *cls,
&coin_sig->eddsa_signature,
&coin->coin_pub.eddsa_pub))
{
- report_row_inconsistency ("payback",
- rowid,
- "coin payback signature invalid");
+ report (report_bad_sig_losses,
+ json_pack ("{s:s, s:I, s:o, s:o}",
+ "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!*/
@@ -921,9 +1072,6 @@ handle_payback_by_reserve (void *cls,
&msig.eddsa_signature,
&master_pub.eddsa_pub))
{
- report_row_inconsistency ("denomination_revocations",
- rev_rowid,
- "master signature invalid");
rev = "master signature invalid";
}
else
@@ -937,6 +1085,19 @@ handle_payback_by_reserve (void *cls,
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,
sizeof (*reserve_pub),
@@ -1833,10 +1994,11 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
&refunds))
{
/* refunds above expenditures? Bad! */
- report_coin_inconsistency (coin_pub,
- &expenditures,
- &refunds,
- "could not subtract refunded amount from expenditures");
+ report_coin_arithmetic_inconsistency ("refund (balance)",
+ coin_pub,
+ &expenditures,
+ &refunds,
+ 0);
return GNUNET_SYSERR;
}
@@ -1847,10 +2009,11 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
&value))
{
/* spent > value */
- report_coin_inconsistency (coin_pub,
- &spent,
- &value,
- "accepted deposits (minus refunds) exceeds denomination value");
+ report_coin_arithmetic_inconsistency ("spend",
+ coin_pub,
+ &spent,
+ &value,
+ -1);
return GNUNET_SYSERR;
}
@@ -1861,10 +2024,11 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
&merchant_loss))
{
/* refunds above deposits? Bad! */
- report_coin_inconsistency (coin_pub,
- merchant_gain,
- &merchant_loss,
- "merchant was granted more refunds than he deposited");
+ report_coin_arithmetic_inconsistency ("refund (merchant)",
+ coin_pub,
+ merchant_gain,
+ &merchant_loss,
+ 0);
return GNUNET_SYSERR;
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -1905,7 +2069,6 @@ wire_transfer_information_cb (void *cls,
{
struct WireCheckContext *wcc = cls;
const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
- struct TALER_Amount contribution;
struct TALER_Amount computed_value;
struct TALER_Amount computed_fees;
struct TALER_Amount coin_value_without_fee;
@@ -1977,9 +2140,11 @@ wire_transfer_information_cb (void *cls,
coin_fee))
{
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- report_row_inconsistency ("aggregation",
- rowid,
- "inconsistent coin value and fee claimed in aggregation");
+ report_amount_arithmetic_inconsistency ("aggregation (fee structure)",
+ rowid,
+ coin_value,
+ coin_fee,
+ -1);
return;
}
if (0 !=
@@ -1987,18 +2152,22 @@ wire_transfer_information_cb (void *cls,
&coin_value_without_fee))
{
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- report_row_inconsistency ("aggregation",
- rowid,
- "coin transaction history and aggregation disagree about coin's contribution");
+ report_amount_arithmetic_inconsistency ("aggregation (contribution)",
+ rowid,
+ &coin_value_without_fee,
+ &computed_value,
+ -1);
}
if (0 !=
TALER_amount_cmp (&computed_fees,
coin_fee))
{
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- report_row_inconsistency ("aggregation",
- rowid,
- "coin transaction history and aggregation disagree about applicable fees");
+ report_amount_arithmetic_inconsistency ("aggregation (fee)",
+ rowid,
+ coin_fee,
+ &computed_fees,
+ 1);
}
edb->free_coin_transaction_list (edb->cls,
tl);
@@ -2032,23 +2201,12 @@ wire_transfer_information_cb (void *cls,
"date given in aggregate does not match wire transfer date");
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 */
if (GNUNET_OK !=
TALER_amount_add (&wcc->total_deposits,
&wcc->total_deposits,
- &contribution))
+ &coin_value_without_fee))
{
GNUNET_break (0);
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)
{
+ /* FIXME: can we provide a more detailed error report? */
report_row_inconsistency ("wire_out",
rowid,
"audit of associated transactions failed");
@@ -2250,9 +2409,11 @@ check_wire_out_cb (void *cls,
&wcc.total_deposits,
wire_fee))
{
- report_row_inconsistency ("wire_out",
- rowid,
- "could not subtract wire fee from total amount");
+ report_amount_arithmetic_inconsistency ("wire out (fee structure)",
+ rowid,
+ &wcc.total_deposits,
+ wire_fee,
+ -1);
return GNUNET_OK;
}
@@ -2852,9 +3013,16 @@ refresh_session_cb (void *cls,
&coin_sig->eddsa_signature,
&coin_pub->eddsa_pub))
{
- report_row_inconsistency ("melt",
- rowid,
- "invalid signature for coin melt");
+ report (report_bad_sig_losses,
+ json_pack ("{s:s, s:I, s:o, s:o}",
+ "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;
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -2952,9 +3120,11 @@ refresh_session_cb (void *cls,
&amount_without_fee))
{
/* refresh_cost > amount_without_fee */
- report_row_inconsistency ("melt",
- rowid,
- "refresh costs exceed value of melt");
+ report_amount_arithmetic_inconsistency ("melt (fee)",
+ rowid,
+ &amount_without_fee,
+ &refresh_cost,
+ -1);
return GNUNET_OK;
}
@@ -3160,9 +3330,16 @@ deposit_cb (void *cls,
&coin_sig->eddsa_signature,
&coin_pub->eddsa_pub))
{
- report_row_inconsistency ("deposit",
- rowid,
- "invalid signature for coin deposit");
+ report (report_bad_sig_losses,
+ json_pack ("{s:s, s:I, s:o, s:o}",
+ "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;
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -3293,9 +3470,16 @@ refund_cb (void *cls,
&merchant_sig->eddsa_sig,
&merchant_pub->eddsa_pub))
{
- report_row_inconsistency ("refund",
- rowid,
- "invalid signature for refund");
+ report (report_bad_sig_losses,
+ json_pack ("{s:s, s:I, s:o, s:o}",
+ "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;
}
@@ -3306,9 +3490,11 @@ refund_cb (void *cls,
amount_with_fee,
&refund_fee))
{
- report_row_inconsistency ("refund",
- rowid,
- "refunded amount smaller than refund fee");
+ report_amount_arithmetic_inconsistency ("refund (fee)",
+ rowid,
+ &amount_without_fee,
+ &refund_fee,
+ -1);
return GNUNET_OK;
}
@@ -3825,7 +4011,22 @@ run (void *cls,
&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 ==
+ TALER_amount_get_zero (currency,
&total_balance_reserve_not_closed));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_get_zero (currency,
+ &total_bad_sig_loss));
GNUNET_assert (NULL !=
(report_emergencies = json_array ()));
GNUNET_assert (NULL !=
@@ -3844,6 +4045,10 @@ run (void *cls,
(report_coin_inconsistencies = json_array ()));
GNUNET_assert (NULL !=
(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 ();
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"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}",
/* blocks of 5 for easier counting/matching to format string */
/* block */
"reserve_balance_insufficient_inconsistencies",
@@ -3911,12 +4118,28 @@ run (void *cls,
"total_wire_out_delta_minus",
TALER_JSON_from_amount (&total_wire_out_delta_minus),
/* block */
+ "bad_sig_losses",
+ report_bad_sig_losses,
+ "total_bad_sig_loss",
+ TALER_JSON_from_amount (&total_bad_sig_loss),
"row_inconsistencies",
report_row_inconsistencies,
"denomination_key_validity_withdraw_inconsistencies",
denomination_key_validity_withdraw_inconsistencies,
"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",
TALER_JSON_from_amount (&total_aggregation_fee_income)
);