implement auditor support for #4960

This commit is contained in:
Christian Grothoff 2022-08-01 13:10:53 +02:00
parent 7698f14d50
commit 368194badd
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
2 changed files with 240 additions and 13 deletions

View File

@ -139,6 +139,10 @@ In that time, the wire auditor processed the following table ranges:
{% endif %}
\end{center}
The total credits to the exchange processed in
this audit run was {\bf {{ wire.total_wire_in }}.
The total debits initiated by the exchange processed in
this audit run was {\bf {{ wire.total_wire_out }}.
\section{Operations}
@ -146,6 +150,16 @@ The balance of the escrow account should
be {\bf {{ coins.total_escrow_balance }}} (coins)
plus {\bf {{ reserves.total_escrow_balance }}} (reserves).
\noindent
This should match the final balance computed from
ingoing and outgoing wire transfers, which is
{\bf {{ wire.final_balance}} }.
\noindent
A total of {\bf {{ wire.total_drained}} } in profits
were transferred (over the lifetime of the exchange)
to non-escrowed accounts.
\noindent
The active operational risk stands at
{\bf {{ coins.total_active_risk }}}.

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2017-2021 Taler Systems SA
Copyright (C) 2017-2022 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
@ -284,6 +284,36 @@ static struct TALER_Amount total_closure_amount_lag;
*/
static struct TALER_Amount total_wire_format_amount;
/**
* Total amount credited to exchange accounts.
*/
static struct TALER_Amount total_wire_in;
/**
* Total amount debited to exchange accounts.
*/
static struct TALER_Amount total_wire_out;
/**
* Total amount of profits drained.
*/
static struct TALER_Amount total_drained;
/**
* Starting balance at the beginning of this iteration.
*/
static struct TALER_Amount start_balance;
/**
* Final balance at the end of this iteration.
*/
static struct TALER_Amount final_balance;
/**
* True if #start_balance was initialized.
*/
static bool had_start_balance;
/**
* Amount of zero in our currency.
*/
@ -367,7 +397,7 @@ struct ReserveOutInfo
* @param value the `struct ReserveInInfo` to free
* @return #GNUNET_OK
*/
static int
static enum GNUNET_GenericReturnValue
free_rii (void *cls,
const struct GNUNET_HashCode *key,
void *value)
@ -392,7 +422,7 @@ free_rii (void *cls,
* @param value the `struct ReserveOutInfo` to free
* @return #GNUNET_OK
*/
static int
static enum GNUNET_GenericReturnValue
free_roi (void *cls,
const struct GNUNET_HashCode *key,
void *value)
@ -417,7 +447,7 @@ free_roi (void *cls,
* @param value the `struct ReserveClosure` to free
* @return #GNUNET_OK
*/
static int
static enum GNUNET_GenericReturnValue
free_rc (void *cls,
const struct GNUNET_HashCode *key,
void *value)
@ -485,6 +515,14 @@ do_shutdown (void *cls)
/* Tested in test-auditor.sh #19 */
GNUNET_JSON_pack_array_steal ("wire_format_inconsistencies",
report_wire_format_inconsistencies),
TALER_JSON_pack_amount ("total_wire_in",
&total_wire_in),
TALER_JSON_pack_amount ("total_wire_out",
&total_wire_out),
TALER_JSON_pack_amount ("total_drained",
&total_drained),
TALER_JSON_pack_amount ("final_balance",
&final_balance),
/* Tested in test-auditor.sh #1 */
TALER_JSON_pack_amount ("total_amount_lag",
&total_amount_lag),
@ -591,7 +629,7 @@ do_shutdown (void *cls)
* @param value the `struct ReserveClosure` to free
* @return #GNUNET_OK
*/
static int
static enum GNUNET_GenericReturnValue
check_pending_rc (void *cls,
const struct GNUNET_HashCode *key,
void *value)
@ -662,6 +700,34 @@ hash_rc (const char *receiver_account,
static enum GNUNET_DB_QueryStatus
commit (enum GNUNET_DB_QueryStatus qs)
{
if (qs >= 0)
{
if (had_start_balance)
{
struct TALER_Amount sum;
TALER_ARL_amount_add (&sum,
&total_wire_in,
&start_balance);
TALER_ARL_amount_subtract (&final_balance,
&sum,
&total_wire_out);
qs = TALER_ARL_adb->update_predicted_result (TALER_ARL_adb->cls,
&TALER_ARL_master_pub,
&final_balance,
&total_drained);
}
else
{
TALER_ARL_amount_subtract (&final_balance,
&total_wire_in,
&total_wire_out);
qs = TALER_ARL_adb->insert_predicted_result (TALER_ARL_adb->cls,
&TALER_ARL_master_pub,
&final_balance,
&total_drained);
}
}
if (0 > qs)
{
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
@ -956,6 +1022,9 @@ wire_out_cb (void *cls,
GNUNET_TIME_timestamp2s (date),
TALER_amount2s (amount),
TALER_B2S (wtid));
TALER_ARL_amount_add (&total_wire_out,
&total_wire_out,
amount);
GNUNET_CRYPTO_hash (wtid,
sizeof (struct TALER_WireTransferIdentifierRawP),
&key);
@ -1133,7 +1202,7 @@ struct CheckMatchContext
* @param key key of @a value in #reserve_closures
* @param value a `struct ReserveClosure`
*/
static int
static enum GNUNET_GenericReturnValue
check_rc_matches (void *cls,
const struct GNUNET_HashCode *key,
void *value)
@ -1163,14 +1232,15 @@ check_rc_matches (void *cls,
/**
* Check whether the given transfer was justified by a reserve closure. If
* not, complain that we failed to match an entry from #out_map. This means a
* wire transfer was made without proper justification.
* 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
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
complain_out_not_found (void *cls,
@ -1195,6 +1265,122 @@ complain_out_not_found (void *cls,
&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)
{
case GNUNET_DB_STATUS_HARD_ERROR:
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:
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))
{
GNUNET_break (0);
TALER_ARL_report (report_row_inconsistencies,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("table",
"profit_drains"),
GNUNET_JSON_pack_uint64 ("row",
serial),
GNUNET_JSON_pack_data_auto ("wtid",
&roi->details.wtid),
GNUNET_JSON_pack_string ("diagnostic",
"invalid signature")));
TALER_ARL_amount_add (&total_bad_amount_out_plus,
&total_bad_amount_out_plus,
&amount);
}
else if (0 !=
strcasecmp (payto_uri,
roi->details.credit_account_uri))
{
TALER_ARL_report (
report_wire_out_inconsistencies,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_uint64 ("row",
serial),
TALER_JSON_pack_amount ("amount_wired",
&roi->details.amount),
TALER_JSON_pack_amount ("amount_wired",
&amount),
GNUNET_JSON_pack_data_auto ("wtid",
&roi->details.wtid),
TALER_JSON_pack_time_abs_human ("timestamp",
roi->details.execution_date.abs_time),
GNUNET_JSON_pack_string ("account",
wa->ai->section_name),
GNUNET_JSON_pack_string ("diagnostic",
"wrong target account")));
TALER_ARL_amount_add (&total_bad_amount_out_plus,
&total_bad_amount_out_plus,
&amount);
}
else if (0 !=
TALER_amount_cmp (&amount,
&roi->details.amount))
{
TALER_ARL_report (
report_wire_out_inconsistencies,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_uint64 ("row",
serial),
TALER_JSON_pack_amount ("amount_justified",
&roi->details.amount),
TALER_JSON_pack_amount ("amount_wired",
&amount),
GNUNET_JSON_pack_data_auto ("wtid",
&roi->details.wtid),
TALER_JSON_pack_time_abs_human ("timestamp",
roi->details.execution_date.abs_time),
GNUNET_JSON_pack_string ("account",
wa->ai->section_name),
GNUNET_JSON_pack_string ("diagnostic",
"profit drain amount incorrect")));
TALER_ARL_amount_add (&total_bad_amount_out_minus,
&total_bad_amount_out_minus,
&roi->details.amount);
TALER_ARL_amount_add (&total_bad_amount_out_plus,
&total_bad_amount_out_plus,
&amount);
}
GNUNET_free (account_section);
GNUNET_free (payto_uri);
break;
}
}
TALER_ARL_report (
report_wire_out_inconsistencies,
GNUNET_JSON_PACK (
@ -1452,7 +1638,7 @@ conclude_credit_history (void)
* @param execution_date when did we receive the funds
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
static int
static enum GNUNET_GenericReturnValue
reserve_in_cb (void *cls,
uint64_t rowid,
const struct TALER_ReservePublicKeyP *reserve_pub,
@ -1471,6 +1657,9 @@ reserve_in_cb (void *cls,
GNUNET_TIME_timestamp2s (execution_date),
TALER_amount2s (credit),
TALER_B2S (reserve_pub));
TALER_ARL_amount_add (&total_wire_in,
&total_wire_in,
credit);
slen = strlen (sender_account_details) + 1;
rii = GNUNET_malloc (sizeof (struct ReserveInInfo) + slen);
rii->rowid = rowid;
@ -1520,7 +1709,7 @@ reserve_in_cb (void *cls,
* @param value the `struct ReserveInInfo` to free
* @return #GNUNET_OK
*/
static int
static enum GNUNET_GenericReturnValue
complain_in_not_found (void *cls,
const struct GNUNET_HashCode *key,
void *value)
@ -1869,7 +2058,7 @@ begin_credit_audit (void)
* @param wtid identifier used for the wire transfer
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
static int
static enum GNUNET_GenericReturnValue
reserve_closed_cb (void *cls,
uint64_t rowid,
struct GNUNET_TIME_Timestamp execution_date,
@ -1936,6 +2125,8 @@ reserve_closed_cb (void *cls,
static enum GNUNET_DB_QueryStatus
begin_transaction (void)
{
enum GNUNET_DB_QueryStatus qs;
if (GNUNET_SYSERR ==
TALER_ARL_edb->preflight (TALER_ARL_edb->cls))
{
@ -1964,6 +2155,28 @@ begin_transaction (void)
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
}
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (TALER_ARL_currency,
&total_drained));
qs = TALER_ARL_adb->get_predicted_balance (TALER_ARL_adb->cls,
&TALER_ARL_master_pub,
&start_balance,
&total_drained);
switch (qs)
{
case GNUNET_DB_STATUS_HARD_ERROR:
GNUNET_break (0);
return qs;
case GNUNET_DB_STATUS_SOFT_ERROR:
GNUNET_break (0);
return qs;
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
had_start_balance = false;
break;
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
had_start_balance = true;
break;
}
for (struct WireAccount *wa = wa_head;
NULL != wa;
wa = wa->next)